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

se.jbee.inject.bootstrap.Link 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.bootstrap;

import static se.jbee.inject.util.Metaclass.metaclass;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import se.jbee.inject.Array;
import se.jbee.inject.DeclarationType;
import se.jbee.inject.Expiry;
import se.jbee.inject.Repository;
import se.jbee.inject.Resource;
import se.jbee.inject.Scope;
import se.jbee.inject.Source;
import se.jbee.inject.Supplier;
import se.jbee.inject.util.Scoped;
import se.jbee.inject.util.Suppliable;

/**
 * Default implementation of the {@link Linker} that creates {@link Suppliable}s.
 * 
 * @author Jan Bernitt ([email protected])
 */
public final class Link {

	public static final Linker> BUILDIN = linker( defaultExpiration() );

	private static Linker> linker( Map expiryByScope ) {
		return new SuppliableLinker( expiryByScope );
	}

	private static IdentityHashMap defaultExpiration() {
		IdentityHashMap map = new IdentityHashMap();
		map.put( Scoped.APPLICATION, Expiry.NEVER );
		map.put( Scoped.INJECTION, Expiry.expires( 1000 ) );
		map.put( Scoped.THREAD, Expiry.expires( 500 ) );
		map.put( Scoped.DEPENDENCY_TYPE, Expiry.NEVER );
		map.put( Scoped.TARGET_INSTANCE, Expiry.NEVER );
		return map;
	}

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

	private static class SuppliableLinker
			implements Linker> {

		private final Map expiryByScope;

		SuppliableLinker( Map expiryByScope ) {
			super();
			this.expiryByScope = expiryByScope;
		}

		@Override
		public Suppliable[] link( Inspector inspector, Module... modules ) {
			return link( disambiguate( bindingsFrom( modules, inspector ) ) );
		}

		private Suppliable[] link( Binding[] bindings ) {
			Map repositories = initRepositories( bindings );
			Suppliable[] suppliables = new Suppliable[bindings.length];
			for ( int i = 0; i < bindings.length; i++ ) {
				Binding binding = bindings[i];
				Scope scope = binding.scope;
				Expiry expiry = expiryByScope.get( scope );
				if ( expiry == null ) {
					expiry = Expiry.NEVER;
				}
				suppliables[i] = suppliableOf( binding, repositories.get( scope ), expiry );
			}
			return suppliables;
		}

		private static  Suppliable suppliableOf( Binding binding, Repository repository,
				Expiry expiration ) {
			return new Suppliable( binding.resource, binding.supplier, repository, expiration,
					binding.source );
		}

		private static Map initRepositories( Binding[] bindings ) {
			Map repositories = new IdentityHashMap();
			for ( Binding i : bindings ) {
				Repository repository = repositories.get( i.scope );
				if ( repository == null ) {
					repositories.put( i.scope, i.scope.init() );
				}
			}
			return repositories;
		}

		/**
		 * Removes those bindings that are ambiguous but also do not clash because of different
		 * {@link DeclarationType}s that replace each other.
		 */
		private static Binding[] disambiguate( Binding[] bindings ) {
			if ( bindings.length <= 1 ) {
				return bindings;
			}
			List> res = new ArrayList>( bindings.length );
			Arrays.sort( bindings );
			res.add( bindings[0] );
			int lastIndependend = 0;
			for ( int i = 1; i < bindings.length; i++ ) {
				Binding one = bindings[lastIndependend];
				Binding other = bindings[i];
				boolean equalResource = one.resource.equalTo( other.resource );
				if ( !equalResource || !other.source.getType().replacedBy( one.source.getType() ) ) {
					res.add( other );
					lastIndependend = i;
				} else if ( one.source.getType().clashesWith( other.source.getType() ) ) {
					throw new IllegalStateException( "Duplicate binds:\n" + one + "\n" + other );
				}
			}
			return Array.of( res, Binding.class );
		}

		private static Binding[] bindingsFrom( Module[] modules, Inspector inspector ) {
			Set> declared = new HashSet>();
			Set> multimodals = new HashSet>();
			ListBindings bindings = new ListBindings();
			for ( Module m : modules ) {
				Class ns = m.getClass();
				final boolean hasBeenDeclared = declared.contains( ns );
				if ( hasBeenDeclared ) {
					if ( !metaclass( ns ).monomodal() ) {
						multimodals.add( ns );
					}
				}
				if ( !hasBeenDeclared || multimodals.contains( ns ) ) {
					m.declare( bindings, inspector );
					declared.add( ns );
				}
			}
			return Array.of( bindings.list, Binding.class );
		}

	}

	private static class ListBindings
			implements Bindings {

		final List> list = new ArrayList>( 100 );

		ListBindings() {
			// make visible
		}

		@Override
		public  void add( Resource resource, Supplier supplier, Scope scope,
				Source source ) {
			list.add( new Binding( resource, supplier, scope, source ) );
		}

	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy