/**
 * Copyright (c) 2010 Sonatype, Inc. All rights reserved.
 *
 * This program is licensed to you under the Apache License Version 2.0,
 * and you may not use this file except in compliance with the Apache License Version 2.0.
 * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the Apache License Version 2.0 is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
 */
package org.sonatype.guice.bean.locators;

import java.lang.annotation.Annotation;
import java.util.Map.Entry;

import javax.inject.Provider;

import com.google.inject.Binding;

/**
 * Qualified bean {@link Entry} and {@link Provider} backed by an injector {@link Binding}.
 */
public final class QualifiedBean<Q extends Annotation, T>
    implements Entry<Q, T>, Provider<T>
{
    // ----------------------------------------------------------------------
    // Constants
    // ----------------------------------------------------------------------

    static final ImplementationVisitor IMPLEMENTATION_VISITOR = new ImplementationVisitor();

    // ----------------------------------------------------------------------
    // Implementation fields
    // ----------------------------------------------------------------------

    private final Q qualifier;

    private final Binding<? extends T> binding;

    private T bean;

    // ----------------------------------------------------------------------
    // Constructors
    // ----------------------------------------------------------------------

    QualifiedBean( final Q qualifier, final Binding<? extends T> binding )
    {
        this.qualifier = qualifier;
        this.binding = binding;
    }

    // ----------------------------------------------------------------------
    // Public methods
    // ----------------------------------------------------------------------

    /**
     * @return Qualified bean binding
     */
    public Binding<? extends T> getBinding()
    {
        return binding;
    }

    @SuppressWarnings( "unchecked" )
    public Class<T> getImplementationClass()
    {
        return (Class<T>) binding.acceptTargetVisitor( IMPLEMENTATION_VISITOR );
    }

    public Q getKey()
    {
        return qualifier;
    }

    public synchronized T getValue()
    {
        if ( null == bean )
        {
            bean = binding.getProvider().get();
        }
        return bean;
    }

    public T setValue( final T value )
    {
        throw new UnsupportedOperationException();
    }

    public T get()
    {
        return getValue();
    }

    @Override
    public String toString()
    {
        return getKey() + "=" + getValue();
    }
}

@@ -861,16 +861,140 @@
 Binding;
+%0Aimport com.google.inject.Scope;%0Aimport com.google.inject.Scopes;%0Aimport com.google.inject.spi.DefaultBindingScopingVisitor;
 %0A%0A/**%0A *
@@ -1145,16 +1145,70 @@
 ion, T%3E%0A
+    extends DefaultBindingScopingVisitor%3CProvider%3CT%3E%3E%0A
     impl
@@ -1749,34 +1749,24 @@
 nal Binding%3C
-? extends 
 T%3E binding;%0A
@@ -1782,14 +1782,34 @@
 ate 
-T bean
+final Provider%3CT%3E provider
 ;%0A%0A 
@@ -2036,26 +2036,16 @@
 Binding%3C
-? extends 
 T%3E bindi
@@ -2114,32 +2114,95 @@
 nding = binding;
+%0A%0A        this.provider = binding.acceptScopingVisitor( this );
 %0A    %7D%0A%0A    // -
@@ -2760,21 +2760,8 @@
 lic 
-synchronized 
 T ge
@@ -2787,86 +2787,31 @@
     
-if ( null == bean )%0A        %7B%0A            bean = binding.getP
+return p
 rovider
-()
 .get();%0A
     
@@ -2810,39 +2810,8 @@
 ();%0A
-        %7D%0A        return bean;%0A
     
@@ -3073,11 +3073,672 @@
 );%0A    %7D
+%0A%0A    @Override%0A    public Provider%3CT%3E visitEagerSingleton()%0A    %7B%0A        return binding.getProvider();%0A    %7D%0A%0A    @Override%0A    public Provider%3CT%3E visitScope( final Scope scope )%0A    %7B%0A        return Scopes.SINGLETON.equals( scope ) ? visitEagerSingleton() : visitOther();%0A    %7D%0A%0A    @Override%0A    public Provider%3CT%3E visitScopeAnnotation( final Class%3C? extends Annotation%3E scopeAnnotation )%0A    %7B%0A        return %22Singleton%22.equals( scopeAnnotation.getSimpleName() ) ? visitEagerSingleton() : visitOther();%0A    %7D%0A%0A    @Override%0A    protected Provider%3CT%3E visitOther()%0A    %7B%0A        return Scopes.SINGLETON.scope( binding.getKey(), binding.getProvider() );%0A    %7D
 %0A%7D%0A

Use Guice's Scopes.SINGLETON logic to cache bean values when iterating (avoids potential deadlocks)