/*******************************************************************************
 * 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.plexus;

import java.net.URL;

import org.codehaus.plexus.component.annotations.Component;
import org.eclipse.sisu.inject.Logs;
import org.eclipse.sisu.space.AnnotationVisitor;
import org.eclipse.sisu.space.ClassSpace;
import org.eclipse.sisu.space.ClassSpaceVisitor;
import org.eclipse.sisu.space.ClassVisitor;
import org.eclipse.sisu.space.LoadedClass;
import org.eclipse.sisu.space.QualifiedTypeVisitor;

/**
 * {@link ClassSpaceVisitor} that reports Plexus bean classes annotated with @{@link Component}.
 */
public final class PlexusTypeVisitor
    implements ClassSpaceVisitor, ClassVisitor
{
    // ----------------------------------------------------------------------
    // Constants
    // ----------------------------------------------------------------------

    private static final String COMPONENT_DESC = 'L' + Component.class.getName().replace( '.', '/' ) + ';';

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

    private final ComponentAnnotationVisitor componentVisitor = new ComponentAnnotationVisitor();

    private final PlexusTypeListener plexusTypeListener;

    private final QualifiedTypeVisitor qualifiedTypeVisitor;

    private ClassSpace space;

    private String source;

    private String implementation;

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

    public PlexusTypeVisitor( final PlexusTypeListener listener )
    {
        plexusTypeListener = listener;
        qualifiedTypeVisitor = new QualifiedTypeVisitor( listener );
    }

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

    public void enterSpace( final ClassSpace _space )
    {
        space = _space;
        source = _space.toString();
        qualifiedTypeVisitor.enterSpace( _space );

        if ( Logs.TRACE_ENABLED )
        {
            QualifiedTypeVisitor.verify( _space, Component.class );
        }
    }

    public ClassVisitor visitClass( final URL url )
    {
        componentVisitor.reset();
        implementation = null;
        qualifiedTypeVisitor.visitClass( url );
        return this;
    }

    public void enterClass( final int modifiers, final String name, final String _extends, final String[] _implements )
    {
        if ( ( modifiers & NON_INSTANTIABLE ) == 0 )
        {
            implementation = name.replace( '/', '.' );
        }
        qualifiedTypeVisitor.enterClass( modifiers, name, _extends, _implements );
    }

    public AnnotationVisitor visitAnnotation( final String desc )
    {
        if ( COMPONENT_DESC.equals( desc ) )
        {
            return componentVisitor;
        }
        return qualifiedTypeVisitor.visitAnnotation( desc );
    }

    public void leaveClass()
    {
        if ( null != implementation )
        {
            final Component component = componentVisitor.getComponent( space );
            if ( null != component )
            {
                final LoadedClass<?> clazz = new LoadedClass<Object>( space.loadClass( implementation ) );
                plexusTypeListener.hear( component, clazz, source );
            }
            else
            {
                qualifiedTypeVisitor.leaveClass();
            }
            implementation = null;
        }
    }

    public void leaveSpace()
    {
        qualifiedTypeVisitor.leaveSpace();
    }

    // ----------------------------------------------------------------------
    // Component annotation scanner
    // ----------------------------------------------------------------------

    static final class ComponentAnnotationVisitor
        implements AnnotationVisitor
    {
        private String role;

        private String hint;

        private String strategy;

        private String description;

        public void reset()
        {
            role = null;
            hint = Hints.DEFAULT_HINT;
            strategy = Strategies.SINGLETON;
            description = "";
        }

        public void enterAnnotation()
        {
            // no-op; maintain results outside of individual annotation scan
        }

        public void visitElement( final String name, final Object value )
        {
            if ( "role".equals( name ) )
            {
                role = (String) value;
            }
            else if ( "hint".equals( name ) )
            {
                hint = Hints.canonicalHint( (String) value );
            }
            else if ( "instantiationStrategy".equals( name ) )
            {
                strategy = (String) value;
            }
            else if ( "description".equals( name ) )
            {
                description = (String) value;
            }
        }

        public void leaveAnnotation()
        {
            // no-op; maintain results outside of individual annotation scan
        }

        public Component getComponent( final ClassSpace space )
        {
            return null != role ? new ComponentImpl( space.loadClass( role ), hint, strategy, description ) : null;
        }
    }
}

@@ -3214,28 +3214,8 @@
 name
-.replace( '/', '.' )
 ;%0A  
@@ -3790,22 +3790,16 @@
   final 
-Loaded
 Class%3C?%3E
@@ -3810,33 +3810,8 @@
 zz =
- new LoadedClass%3CObject%3E(
  spa
@@ -3838,16 +3838,34 @@
 entation
+.replace( '/', '.'
  ) );%0A  
@@ -3913,22 +3913,49 @@
 mponent,
+ new LoadedClass%3CObject%3E(
  clazz
+ )
 , source
@@ -3970,34 +3970,54 @@
         
-%7D%0A
     
-        else
+qualifiedTypeVisitor.disqualify();
 %0A       
@@ -4013,33 +4013,33 @@
 ();%0A            
-%7B
+%7D
 %0A               
@@ -4023,32 +4023,34 @@
       %7D%0A        
+%7D%0A
         qualifie
@@ -4080,67 +4080,8 @@
 ();%0A
-            %7D%0A            implementation = null;%0A        %7D%0A
     

Use new disqualify method to avoid registering @Component implementations again as @Named if they happen to have both annotations, rather than not telling the qualifiedTypeVisitor that we're leaving the class. Also remove unnecessary reset of implementation field (will be reset on next enter).