/*******************************************************************************
 * Copyright (c) 2010, 2013 Sonatype, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Stuart McCulloch (Sonatype, Inc.) - initial API and implementation
 *******************************************************************************/
package org.eclipse.sisu.space;

import java.net.URL;

import javax.inject.Qualifier;

import org.eclipse.sisu.inject.Logs;

import com.google.inject.Module;

/**
 * {@link ClassSpaceVisitor} that reports types annotated with {@link Qualifier} annotations.
 */
public final class QualifiedTypeVisitor
    implements ClassSpaceVisitor, ClassVisitor
{
    // ----------------------------------------------------------------------
    // Implementation fields
    // ----------------------------------------------------------------------

    private final QualifierCache qualifierCache = new QualifierCache();

    private final QualifiedTypeListener listener;

    private ClassSpace space;

    private URL location;

    private String source;

    private String clazzName;

    private boolean qualified;

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

    public QualifiedTypeVisitor( final QualifiedTypeListener listener )
    {
        this.listener = listener;
    }

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

    public static boolean verify( final ClassSpace space, final Class<?>... specification )
    {
        for ( final Class<?> expectedClazz : specification )
        {
            try
            {
                final Class<?> spaceClazz = space.loadClass( expectedClazz.getName() );
                if ( spaceClazz != expectedClazz )
                {
                    Logs.warn( "Inconsistent ClassLoader for: {} in: {}", expectedClazz, space );
                    Logs.warn( "Expected: {} saw: {}", expectedClazz.getClassLoader(), spaceClazz.getClassLoader() );
                }
            }
            catch ( final TypeNotPresentException e )
            {
                if ( expectedClazz.isAnnotation() )
                {
                    Logs.trace( "Potential problem: {} is not visible from: {}", expectedClazz, space );
                }
            }
        }
        return true;
    }

    public void enterSpace( final ClassSpace _space )
    {
        space = _space;
        source = null;

        if ( Logs.TRACE_ENABLED )
        {
            verify( _space, Qualifier.class, Module.class );
        }
    }

    public ClassVisitor visitClass( final URL url )
    {
        location = url;
        clazzName = null;
        qualified = false;

        return this;
    }

    public void enterClass( final int modifiers, final String name, final String _extends, final String[] _implements )
    {
        if ( ( modifiers & NON_INSTANTIABLE ) == 0 )
        {
            clazzName = name; // concrete type
        }
    }

    public AnnotationVisitor visitAnnotation( final String desc )
    {
        if ( null != clazzName )
        {
            qualified = qualified || qualifierCache.qualify( space, desc );
        }
        return null;
    }

    public void leaveClass()
    {
        if ( qualified )
        {
            qualified = false; // we might be called twice (once per-class, once per-space)

            // compressed record of class location
            final String path = location.getPath();
            if ( null == source || !path.startsWith( source ) )
            {
                final int i = path.indexOf( clazzName );
                source = i <= 0 ? path : path.substring( 0, i );
            }

            listener.hear( space.loadClass( clazzName.replace( '/', '.' ) ), source );
        }
    }

    public void leaveSpace()
    {
        // no-op
    }
}

@@ -3635,18 +3635,18 @@
 oid 
-leaveClass
+disqualify
 ()%0A 
@@ -3657,21 +3657,16 @@
 %0A       
- if (
  qualifi
@@ -3668,39 +3668,75 @@
 alified 
-)
+= false;
 %0A    
+%7D%0A%0A
     
-%7B
+public void leaveClass()
 %0A   
+ %7B%0A
         
  qualifi
@@ -3719,32 +3719,36 @@
 )%0A    %7B%0A        
+if (
  qualified = fal
@@ -3746,78 +3746,19 @@
 ied 
-= false; // we might be called twice (once per-class, once per-space)%0A
+)%0A        %7B
 %0A   

Add method to disqualify previously qualified classes and remove unnecessary reset of flag (leave method is no longer called twice now it's been separated)