All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.simpleflatmapper.reflect.meta.ObjectPropertyFinder Maven / Gradle / Ivy

package org.simpleflatmapper.reflect.meta;

import org.simpleflatmapper.reflect.InstantiatorDefinition;
import org.simpleflatmapper.reflect.Parameter;
import org.simpleflatmapper.reflect.property.EligibleAsNonMappedProperty;
import org.simpleflatmapper.reflect.property.OptionalProperty;
import org.simpleflatmapper.util.BooleanProvider;

import java.lang.reflect.Type;
import java.util.*;

final class ObjectPropertyFinder extends PropertyFinder {


	enum State {
		NONE, SELF, PROPERTIES
	}
	private final List eligibleInstantiatorDefinitions;
	private final ObjectClassMeta classMeta;
	private final Map, PropertyFinder> subPropertyFinders = new HashMap, PropertyFinder>();
	private State state = State.NONE;
	private String selfName;



    ObjectPropertyFinder(ObjectClassMeta classMeta, boolean selfScoreFullName) {
        super(selfScoreFullName);
        this.classMeta = classMeta;
		this.eligibleInstantiatorDefinitions = classMeta.getInstantiatorDefinitions() != null ? new ArrayList(classMeta.getInstantiatorDefinitions()) : null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public void lookForProperties(final PropertyNameMatcher propertyNameMatcher,
								  Object[] properties, FoundProperty matchingProperties,
								  PropertyMatchingScore score,
								  boolean allowSelfReference,
								  PropertyFinderTransformer propertyFinderTransform,
								  TypeAffinityScorer typeAffinityScorer, PropertyFilter propertyFilter) {
		lookForConstructor(propertyNameMatcher, properties, matchingProperties, score, propertyFinderTransform, typeAffinityScorer, propertyFilter);
		lookForProperty(propertyNameMatcher, properties, matchingProperties, score, propertyFinderTransform, typeAffinityScorer, propertyFilter);

		final String propName = propertyNameMatcher.toString();
		if (allowSelfReference 
				&& !disallowSelfReference(properties) 
				&& (state == State.NONE || (state == State.SELF && propName.equals(selfName)))) {
			SelfPropertyMeta propertyMeta = new SelfPropertyMeta(classMeta.getReflectionService(), classMeta.getType(), new BooleanProvider() {
				@Override
				public boolean getBoolean() {
					return state != State.PROPERTIES;
				}
			}, properties, propertyNameMatcher.toString(), classMeta);
			if (propertyFilter.testProperty(propertyMeta)) {
				matchingProperties.found(propertyMeta,
						selfPropertySelectionCallback(propName),
						score.self(classMeta, propName),
						typeAffinityScorer);
			}
		}

		if (isOptionalAndEligibleAsNonMappedProperty(properties)) {
			NonMappedPropertyMeta meta = new NonMappedPropertyMeta(propertyNameMatcher.toString(), classMeta.getType(), classMeta.getReflectionService(), properties);
			matchingProperties.found(
					meta,
					new Runnable() {
						@Override
						public void run() {
						}
					},
					score.notMatch(),
					typeAffinityScorer);
		}
	}

	public static boolean isOptionalAndEligibleAsNonMappedProperty(Object[] properties) {
		return containsProperty(properties, OptionalProperty.class) && containsProperty(properties, EligibleAsNonMappedProperty.class);
	}

	public static boolean containsProperty(Object[] properties, Class propClass) {
		for(Object p : properties) {
			if (propClass.isAssignableFrom(p.getClass())) {
				return true;
			}
		}
		return false;

	}

	private boolean disallowSelfReference(Object[] properties) {
    	if (classMeta.getNumberOfProperties() <= 1) return false;
    	return containsProperty(properties, DisallowSelfReference.class);
	}

	private void lookForConstructor(final PropertyNameMatcher propertyNameMatcher, Object[] properties, final FoundProperty matchingProperties, final PropertyMatchingScore score, final PropertyFinderTransformer propertyFinderTransformer, TypeAffinityScorer typeAffinityScorer, PropertyFilter propertyFilter) {
		if (classMeta.getConstructorProperties() != null) {
			for (final ConstructorPropertyMeta prop : classMeta.getConstructorProperties()) {
				final String columnName = getColumnName(prop);
				PropertyNameMatch matches = propertyNameMatcher.matches(columnName);
				if (matches != null
						&& hasConstructorMatching(prop.getParameter())) {
					if (propertyFilter.testProperty(prop)) {
						matchingProperties.found(prop, propertiesRemoveNonMatchingCallBack(prop), score.matches(matches), typeAffinityScorer);
					}
				}
				if (propertyFilter.testPath(prop)) {
					PropertyNameMatch partialMatch = propertyNameMatcher.partialMatch(columnName);
					if (partialMatch != null && hasConstructorMatching(prop.getParameter())) {
						PropertyNameMatcher subPropMatcher = partialMatch.getLeftOverMatcher();
						lookForSubProperty(subPropMatcher, properties, prop, new FoundProperty() {
							@Override
							public void found(final PropertyMeta propertyMeta, final Runnable selectionCallback, final PropertyMatchingScore score, TypeAffinityScorer typeAffinityScorer) {
								matchingProperties.found(
										new SubPropertyMeta(classMeta.getReflectionService(), prop, propertyMeta),
										propertiesDelegateAndRemoveNonMatchingCallBack(selectionCallback, prop), score, typeAffinityScorer);
							}
						}, score.matches(partialMatch), propertyFinderTransformer, typeAffinityScorer, propertyFilter);
					}
				}
			}
		}
	}


	private void lookForProperty(final PropertyNameMatcher propertyNameMatcher, Object[] properties, final FoundProperty matchingProperties, final PropertyMatchingScore score, final PropertyFinderTransformer propertyFinderTransformer, TypeAffinityScorer typeAffinityScorer, PropertyFilter propertyFilter) {
		for (final PropertyMeta prop : classMeta.getProperties()) {
			final String columnName =
					hasAlias(properties)
							? prop.getName()
							: getColumnName(prop);
			PropertyNameMatch matches = propertyNameMatcher.matches(columnName);
			if (matches != null) {
				if (propertyFilter.testProperty(prop)) {
					matchingProperties.found(prop, propertiesCallBack(), score.matches(matches), typeAffinityScorer);
				}
			}
			if (propertyFilter.testPath(prop)) {
				final PropertyNameMatch subPropMatch = propertyNameMatcher.partialMatch(columnName);
				if (subPropMatch != null) {
					final PropertyNameMatcher subPropMatcher = subPropMatch.getLeftOverMatcher();
					lookForSubProperty(subPropMatcher, properties, prop, new FoundProperty() {
								@Override
								public void found(final PropertyMeta propertyMeta, final Runnable selectionCallback, final PropertyMatchingScore score, TypeAffinityScorer typeAffinityScorer) {
									matchingProperties.found(new SubPropertyMeta(classMeta.getReflectionService(), prop, propertyMeta),
											propertiesDelegateCallBack(selectionCallback), score, typeAffinityScorer);
								}
							}, score.matches(subPropMatch),
							propertyFinderTransformer, typeAffinityScorer, propertyFilter);
				}
			}
		}
	}

	private boolean hasAlias(Object[] properties) {
    	for(Object o : properties) {
    		if ("org.simpleflatmapper.map.property.RenameProperty".equals(o.getClass().getName())) // not so great... well
				return true;
		}
		return false;
	}

	private void lookForSubProperty(
			final PropertyNameMatcher propertyNameMatcher,
			Object[] properties, final PropertyMeta prop,
			final FoundProperty foundProperty,
			final PropertyMatchingScore score,
			final PropertyFinderTransformer propertyFinderTransformer, TypeAffinityScorer typeAffinityScorer, PropertyFilter propertyFilter) {

    	PropertyFinder subPropertyFinder = subPropertyFinders.get(prop);

    	final PropertyFinder newPropertyFinder;

		if (subPropertyFinder == null) {
			subPropertyFinder = prop.getPropertyClassMeta().newPropertyFinder();
			newPropertyFinder = subPropertyFinder;
		} else {
			newPropertyFinder = null;
		}

		propertyFinderTransformer
				.apply(subPropertyFinder)
				.lookForProperties(propertyNameMatcher, properties, new FoundProperty() {
					@Override
					public void found(final PropertyMeta propertyMeta, final Runnable selectionCallback, final PropertyMatchingScore score, TypeAffinityScorer typeAffinityScorer) {
						if (newPropertyFinder != null) {
							subPropertyFinders.put(prop, newPropertyFinder);
						}
						foundProperty.found(propertyMeta, selectionCallback, score, typeAffinityScorer);
					}
				}, score, false, propertyFinderTransformer, typeAffinityScorer, propertyFilter);
	}

    private String getColumnName(PropertyMeta prop) {
        return this.classMeta.getAlias(prop.getName());
    }


    private void removeNonMatching(Parameter param) {
		ListIterator li = eligibleInstantiatorDefinitions.listIterator();
		while(li.hasNext()){
			InstantiatorDefinition cd = li.next();
			if (!cd.hasParam(param)) {
				li.remove();
			}
		}
	}

	private boolean hasConstructorMatching(Parameter param) {
		for(InstantiatorDefinition cd : eligibleInstantiatorDefinitions) {
			if (cd.hasParam(param)) {
				return true;
			}
		}
		return false;
	}

	private Runnable compose(final Runnable r1, final Runnable r2) {
		return new Runnable() {
			@Override
			public void run() {
				r1.run();
				r2.run();
			}
		};
	}

	private Runnable propertiesDelegateAndRemoveNonMatchingCallBack(final Runnable selectionCallback, final ConstructorPropertyMeta prop) {
		return compose(selectionCallback, propertiesRemoveNonMatchingCallBack(prop));
	}

	private Runnable propertiesRemoveNonMatchingCallBack(final ConstructorPropertyMeta prop) {
		return compose(removeNonMatchingCallBack(prop), propertiesCallBack());
	}

	private Runnable removeNonMatchingCallBack(final ConstructorPropertyMeta prop) {
		return new Runnable() {
			@Override
			public void run() {
				removeNonMatching(prop.getParameter());
			}
		};
	}

	private Runnable propertiesDelegateCallBack(final Runnable selectionCallback) {
		return compose(selectionCallback, propertiesCallBack());
	}


	private Runnable propertiesCallBack() {
		return new Runnable() {
			@Override
			public void run() {
				state = State.PROPERTIES;
			}
		};
	}

	private Runnable selfPropertySelectionCallback(final String propName) {
		return new Runnable() {
			@Override
			public void run() {
				state = State.SELF;
				selfName = propName;
			}
		};
	}

	@Override
	public List getEligibleInstantiatorDefinitions() {
		return eligibleInstantiatorDefinitions;
	}

	@Override
	public PropertyFinder getSubPropertyFinder(PropertyMeta owner) {
		return subPropertyFinders.get(owner);
	}

	@Override
	public PropertyFinder getOrCreateSubPropertyFinder(SubPropertyMeta subPropertyMeta) {
		PropertyFinder propertyFinder = subPropertyFinders.get(subPropertyMeta.getOwnerProperty());
		
		if (propertyFinder == null) {
			propertyFinder = subPropertyMeta.getSubProperty().getPropertyClassMeta().newPropertyFinder();
			subPropertyFinders.put(subPropertyMeta.getOwnerProperty(), propertyFinder);
		}
		
		return propertyFinder;
	}

	@Override
	public Type getOwnerType() {
		return classMeta.getType();
	}

	@Override
	public void manualMatch(PropertyMeta prop) {
    	if (prop.isConstructorProperty()) {
			removeNonMatching(((ConstructorPropertyMeta) prop).getParameter());
		}
		super.manualMatch(prop);
	}


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy