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

se.jbee.inject.util.Inject Maven / Gradle / Ivy

There is a newer version: 0.6
Show newest version
/*
 *  Copyright (c) 2012, Jan Bernitt 
 *			
 *  Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0
 */
package se.jbee.inject.util;

import static se.jbee.inject.Emergence.emergence;
import static se.jbee.inject.Type.raw;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import se.jbee.inject.Array;
import se.jbee.inject.DIRuntimeException.NoSuchResourceException;
import se.jbee.inject.Demand;
import se.jbee.inject.Dependency;
import se.jbee.inject.Expiry;
import se.jbee.inject.Injectable;
import se.jbee.inject.Injector;
import se.jbee.inject.Injectron;
import se.jbee.inject.Precision;
import se.jbee.inject.Repository;
import se.jbee.inject.Resource;
import se.jbee.inject.Source;
import se.jbee.inject.Supplier;
import se.jbee.inject.Type;

/**
 * Utility to create/use the core containers {@link Injector} and {@link Injectron}.
 * 
 * @author Jan Bernitt ([email protected])
 */
public final class Inject {

	public static Injector from( InjectronSource source ) {
		return new SourcedInjector( source );
	}

	public static  Injectable asInjectable( Supplier supplier, Injector injector ) {
		return new SupplierToInjectable( supplier, injector );
	}

	public static  Injectron injectron( Injectable injectable, Resource resource,
			Demand demand, Expiry expiry, Repository repository, Source source ) {
		return new StaticInjectron( resource, source, demand, expiry, repository, injectable );
	}

	private static class SupplierToInjectable
			implements Injectable {

		private final Supplier supplier;
		private final Injector context;

		SupplierToInjectable( Supplier supplier, Injector context ) {
			super();
			this.supplier = supplier;
			this.context = context;
		}

		@Override
		public T instanceFor( Demand demand ) {
			return supplier.supply( demand.getDependency(), context );
		}
	}

	private Inject() {
		throw new UnsupportedOperationException( "util" );
	}

	/**
	 * The default {@link Injector} that gets the initial {@link Injectron}s from a
	 * {@link InjectronSource}.
	 * 
	 * @author Jan Bernitt ([email protected])
	 */
	public static final class SourcedInjector
			implements Injector {

		private final Map, Injectron[]> injectrons;

		SourcedInjector( InjectronSource source ) {
			super();
			this.injectrons = initFrom( source );
		}

		private Map, Injectron[]> initFrom( InjectronSource source ) {
			Injectron[] injectrons = source.exportTo( this );
			Arrays.sort( injectrons, Precision.RESOURCE_COMPARATOR );
			Map, Injectron[]> map = new IdentityHashMap, Injectron[]>(
					injectrons.length );
			if ( injectrons.length == 0 ) {
				return map;
			}
			Class lastRawType = injectrons[0].getResource().getType().getRawType();
			int start = 0;
			for ( int i = 0; i < injectrons.length; i++ ) {
				Class rawType = injectrons[i].getResource().getType().getRawType();
				if ( rawType != lastRawType ) {
					map.put( lastRawType, Arrays.copyOfRange( injectrons, start, i ) );
					start = i;
				}
				lastRawType = rawType;
			}
			map.put( lastRawType, Arrays.copyOfRange( injectrons, start, injectrons.length ) );
			return map;
		}

		@SuppressWarnings ( "unchecked" )
		@Override
		public  T resolve( Dependency dependency ) {
			final Type type = dependency.getType();
			final boolean array = type.isUnidimensionalArray();
			Injectron injectron = applicableInjectron( dependency );
			if ( injectron != null ) {
				return injectron.instanceFor( dependency );
			}
			if ( array ) {
				return resolveArray( dependency, type.elementType() );
			}
			if ( type.getRawType() == Injectron.class ) {
				Injectron i = applicableInjectron( dependency.onTypeParameter() );
				if ( i != null ) {
					return (T) i;
				}
			}
			if ( type.getRawType() == Injector.class ) {
				return (T) this;
			}
			throw noInjectronFor( dependency );
		}

		private  Injectron applicableInjectron( Dependency dependency ) {
			return mostPreciseOf( typeInjectrons( dependency.getType() ), dependency );
		}

		private static  Injectron mostPreciseOf( Injectron[] injectrons,
				Dependency dependency ) {
			if ( injectrons == null ) {
				return null;
			}
			for ( int i = 0; i < injectrons.length; i++ ) {
				Injectron injectron = injectrons[i];
				if ( injectron.getResource().isApplicableFor( dependency ) ) {
					return injectron;
				}
			}
			return null;
		}

		private  NoSuchResourceException noInjectronFor( Dependency dependency ) {
			return new NoSuchResourceException( dependency, typeInjectrons( dependency.getType() ) );
		}

		private  T resolveArray( Dependency dependency, Type elementType ) {
			if ( elementType.getRawType() == Injectron.class ) {
				return resolveInjectronArray( dependency, elementType.parameter( 0 ) );
			}
			Injectron[] elementInjectrons = typeInjectrons( elementType );
			if ( elementInjectrons != null ) {
				List elements = new ArrayList( elementInjectrons.length );
				addAllApplicable( elements, dependency, elementType, elementInjectrons );
				return toArray( elements, elementType );
			}
			// if there hasn't been binds to that specific wildcard Type  
			if ( elementType.isLowerBound() ) { // wildcard dependency:
				List elements = new ArrayList();
				for ( Entry, Injectron[]> e : injectrons.entrySet() ) {
					if ( Type.raw( e.getKey() ).isAssignableTo( elementType ) ) {
						//FIXME some of the injectrons are just bridges and such - no real values - recursion causes errors here
						@SuppressWarnings ( "unchecked" )
						Injectron[] value = (Injectron[]) e.getValue();
						addAllApplicable( elements, dependency, elementType, value );
					}
				}
				return toArray( elements, elementType );
			}
			throw noInjectronFor( dependency );
		}

		private  T resolveInjectronArray( Dependency dependency, Type instanceType ) {
			Dependency instanceDependency = dependency.typed( instanceType );
			if ( instanceType.isLowerBound() ) {
				List> res = new ArrayList>();
				for ( Entry, Injectron[]> e : injectrons.entrySet() ) {
					if ( raw( e.getKey() ).isAssignableTo( instanceType ) ) {
						@SuppressWarnings ( "unchecked" )
						Injectron[] typeInjectrons = (Injectron[]) e.getValue();
						for ( Injectron i : typeInjectrons ) {
							if ( i.getResource().isSuitableFor( instanceDependency ) ) {
								res.add( i );
							}
						}
					}
				}
				return toArray( res, raw( Injectron.class ) );
			}
			Injectron[] res = typeInjectrons( instanceType );
			List> elements = new ArrayList>( res.length );
			for ( Injectron i : res ) {
				if ( i.getResource().isSuitableFor( instanceDependency ) ) {
					elements.add( i );
				}
			}
			return toArray( elements, raw( Injectron.class ) );
		}

		private static  void addAllApplicable( List elements, Dependency dependency,
				Type elementType, Injectron[] elementInjectrons ) {
			Dependency elementDependency = dependency.typed( elementType );
			for ( int i = 0; i < elementInjectrons.length; i++ ) {
				Injectron injectron = elementInjectrons[i];
				if ( injectron.getResource().isApplicableFor( elementDependency ) ) {
					elements.add( injectron.instanceFor( elementDependency ) );
				}
			}
		}

		@SuppressWarnings ( "unchecked" )
		private static  T toArray( List elements, Type elementType ) {
			return (T) Array.of( elements, elementType.getRawType() );
		}

		@SuppressWarnings ( "unchecked" )
		private  Injectron[] typeInjectrons( Type type ) {
			return (Injectron[]) injectrons.get( type.getRawType() );
		}

		@Override
		public String toString() {
			StringBuilder b = new StringBuilder();
			for ( Entry, Injectron[]> e : injectrons.entrySet() ) {
				b.append( e.getKey() ).append( '\n' );
				for ( Injectron i : e.getValue() ) {
					b.append( '\t' ).append( i ).append( '\n' );
				}
			}
			return b.toString();
		}
	}

	private static class StaticInjectron
			implements Injectron {

		private final Resource resource;
		private final Source source;
		private final Demand demand;
		private final Repository repository;
		private final Injectable injectable;
		private final Expiry expiry;

		StaticInjectron( Resource resource, Source source, Demand demand, Expiry expiry,
				Repository repository, Injectable injectable ) {
			super();
			this.resource = resource;
			this.source = source;
			this.demand = demand;
			this.expiry = expiry;
			this.repository = repository;
			this.injectable = injectable;
		}

		@Override
		public Resource getResource() {
			return resource;
		}

		@Override
		public Source getSource() {
			return source;
		}

		@Override
		public Expiry getExpiry() {
			return expiry;
		}

		@Override
		public T instanceFor( Dependency dependency ) {
			return repository.serve( demand.from( dependency.injectingInto( emergence(
					resource.getInstance(), expiry ) ) ), injectable );
		}

		@Override
		public String toString() {
			return demand.toString() + resource.getTarget().toString() + " " + source.toString();
		}
	}

}