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

com.github.nill14.utils.init.inject.DependencyUtils Maven / Gradle / Ivy

The newest version!
package com.github.nill14.utils.init.inject;

import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;

import javax.inject.Provider;

import com.github.nill14.utils.init.api.BindingKey;
import com.github.nill14.utils.init.api.IBeanDescriptor;
import com.github.nill14.utils.init.api.IBeanInjector;
import com.github.nill14.utils.init.api.IMemberDescriptor;
import com.github.nill14.utils.init.api.IParameterType;
import com.github.nill14.utils.init.api.IPojoFactory;
import com.github.nill14.utils.init.api.IQualifiedProvider;
import com.github.nill14.utils.init.impl.MethodPojoFactory;
import com.github.nill14.utils.init.impl.ProviderTypePojoFactory;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.reflect.TypeToken;

public enum DependencyUtils {
	;
	
	public static Map collectDependencies(IBeanDescriptor descriptor) {
		return ImmutableMap.copyOf(transformDependencies(collectDependencyTypes(descriptor)));
	}
	
	public static Map collectDependencies(IMemberDescriptor descriptor) {
		return ImmutableMap.copyOf(transformDependencies(descriptor.getParameterTypes().stream()));
	}
	
	private static Stream collectDependencyTypes(IBeanDescriptor descriptor) {
		Stream members = Stream.concat(descriptor.getFieldDescriptors().stream(), 
				Stream.concat(descriptor.getMethodDescriptors().stream(), 
						descriptor.getConstructorDescriptors().stream()));
		return members
				.flatMap(f -> f.getParameterTypes().stream());
	}
	
	private static Map transformDependencies(Stream types) {
		Map result = Maps.newHashMap();
		
		Iterable iterable = types::iterator;
		for (IParameterType type : iterable) {
			boolean isRequired = isRequired(type);
			
			while (type.isCollection() || type.isOptional() || isProvider(type) || isQualifiedProvider(type)) { 
				isRequired &= isRequired(type);
				type = type.getFirstParamType();
			} 
			
			Boolean prevRequired = result.get(type);
			// if value is not stored yet
			// or prev value is false and current is true then result is true
			if (prevRequired == null || (isRequired && !prevRequired)) {
				result.put(type, isRequired);
			}
		}

		return result;
	}

	private static boolean isRequired(IParameterType type) {
		return !type.isCollection() && !type.isOptional() && !type.isNullable() && !isQualifiedProvider(type);
	}
	
	private static boolean isProvider(IParameterType type) {
		return Provider.class.equals(type.getRawType());
	}
	
	private static boolean isQualifiedProvider(IParameterType type) {
		return IQualifiedProvider.class.equals(type.getRawType());
	}
	
	private static boolean isBeanInjector(IParameterType type) {
		return IBeanInjector.class.equals(type.getRawType());
	}
	
	
	private static void mergeDependencies(Set> requiredDependencies, Set> optionalDependencies, 
			Map pojoDependencies, Predicate> requiresAnalyse, boolean parentRequired) {
		
		for (Entry dep : pojoDependencies.entrySet()) {
			boolean isRequired = parentRequired && dep.getValue();
			IParameterType type = dep.getKey();
			TypeToken token = type.getToken();
			
			if (!isExcludedFromDependencies(type)) {
				if (isRequired) {
					requiredDependencies.add(token);
				} else {
					optionalDependencies.add(token);
				}
			}

			if (requiresAnalyse.test(type.getBindingKey())) {
				IBeanDescriptor typeDescriptor = new PojoInjectionDescriptor<>(type);
				Map pojoDependencies2 = DependencyUtils.collectDependencies(typeDescriptor);
				mergeDependencies(requiredDependencies, optionalDependencies, pojoDependencies2, requiresAnalyse, isRequired);
			}
		}
	}
	
	
	@SuppressWarnings("rawtypes")
	private static void mergeDependencies(Set> requiredDependencies, Set> optionalDependencies, Predicate> requiresAnalyse, IPojoFactory pojoFactory) {
		
		if (pojoFactory instanceof ProviderTypePojoFactory) {
			IPojoFactory nestedPojoFactory = ((ProviderTypePojoFactory) pojoFactory).getNestedPojoFactory();
			mergeDependencies(requiredDependencies, optionalDependencies, requiresAnalyse, nestedPojoFactory);
		
		} else if (pojoFactory instanceof MethodPojoFactory) {
			Map collectDependencies = DependencyUtils.collectDependencies(
					((MethodPojoFactory) pojoFactory).getMethodDescriptor()); 
			mergeDependencies(requiredDependencies, optionalDependencies, collectDependencies, requiresAnalyse, true);
		}
		
		Map pojoDependencies = DependencyUtils.collectDependencies(pojoFactory.getDescriptor());
		mergeDependencies(requiredDependencies, optionalDependencies, pojoDependencies, requiresAnalyse, true);
	}
	
	private static boolean isExcludedFromDependencies(IParameterType type) {
		return isBeanInjector(type) || isQualifiedProvider(type) || isProvider(type);
	}
	
	/**
	 * 
	 * @param pojoFactories 
	 * @param requiresAnalyse 
	 * @return a map where key is a dependency (e.g. @Inject) and value is whether the dependency is required. 
	 */
	public static Map, Boolean> collectDependencies(Collection> pojoFactories, Predicate> requiresAnalyse) {
		Set> requiredDependencies = Sets.newHashSet();
		Set> optionalDependencies = Sets.newHashSet();
		
		for (IPojoFactory pojoFactory : pojoFactories) {
			mergeDependencies(requiredDependencies, optionalDependencies, requiresAnalyse, pojoFactory);
		}
		
		optionalDependencies.removeAll(requiredDependencies);
		ImmutableMap.Builder, Boolean> builder = ImmutableMap.builder(); 
		for (TypeToken token : optionalDependencies) {
			builder.put(token, false);
		}
		for (TypeToken token : requiredDependencies) {
			builder.put(token, true);
		}
		
		return builder.build();
	}
	
	public static void splitDependencies(Map, Boolean> dependencies, Set> requiredDependencies, Set> optionalDependencies) {
		
		for (Entry, Boolean> dep : dependencies.entrySet()) {
			boolean isRequired = dep.getValue();
			TypeToken token = dep.getKey();
			if (isRequired) {
				requiredDependencies.add(token.getRawType());
			} else {
				optionalDependencies.add(token.getRawType());
			}
		}
	}
}