| /******************************************************************************* |
| * Copyright (c) 2010, 2015 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.io.InputStream; |
| import java.lang.annotation.Annotation; |
| import java.net.URL; |
| import java.util.Enumeration; |
| |
| import org.eclipse.sisu.inject.Logs; |
| import org.eclipse.sisu.space.asm.ClassReader; |
| import org.eclipse.sisu.space.asm.Opcodes; |
| import org.eclipse.sisu.space.asm.Type; |
| |
| /** |
| * Makes a {@link SpaceVisitor} visit a {@link ClassSpace}; can be directed by an optional {@link ClassFinder}. |
| */ |
| public final class SpaceScanner |
| { |
| // ---------------------------------------------------------------------- |
| // Constants |
| // ---------------------------------------------------------------------- |
| |
| private static final int ASM_FLAGS = ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; |
| |
| static final ClassFinder DEFAULT_FINDER = new DefaultClassFinder(); |
| |
| // ---------------------------------------------------------------------- |
| // Implementation fields |
| // ---------------------------------------------------------------------- |
| |
| private final ClassSpace space; |
| |
| private final ClassFinder finder; |
| |
| // ---------------------------------------------------------------------- |
| // Constructors |
| // ---------------------------------------------------------------------- |
| |
| public SpaceScanner( final ClassSpace space, final ClassFinder finder ) |
| { |
| this.space = space; |
| this.finder = finder; |
| } |
| |
| public SpaceScanner( final ClassSpace space ) |
| { |
| this( space, DEFAULT_FINDER ); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // Public methods |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * Makes the given {@link SpaceVisitor} visit the {@link ClassSpace} of this scanner. |
| * |
| * @param visitor The class space visitor |
| */ |
| public void accept( final SpaceVisitor visitor ) |
| { |
| visitor.enterSpace( space ); |
| |
| for ( final Enumeration<URL> result = finder.findClasses( space ); result.hasMoreElements(); ) |
| { |
| final URL url = result.nextElement(); |
| final ClassVisitor cv = visitor.visitClass( url ); |
| if ( null != cv ) |
| { |
| accept( cv, url ); |
| } |
| } |
| |
| visitor.leaveSpace(); |
| } |
| |
| /** |
| * Makes the given {@link ClassVisitor} visit the class contained in the resource {@link URL}. |
| * |
| * @param visitor The class space visitor |
| * @param url The class resource URL |
| */ |
| public static void accept( final ClassVisitor visitor, final URL url ) |
| { |
| if ( null == url ) |
| { |
| return; // nothing to visit |
| } |
| try |
| { |
| final InputStream in = url.openStream(); |
| try |
| { |
| new ClassReader( in ).accept( adapt( visitor ), ASM_FLAGS ); |
| } |
| finally |
| { |
| in.close(); |
| } |
| } |
| catch ( final ArrayIndexOutOfBoundsException e ) // NOPMD |
| { |
| // ignore broken class constant pool in icu4j |
| } |
| catch ( final Exception e ) |
| { |
| Logs.trace( "Problem scanning: {}", url, e ); |
| } |
| } |
| |
| /** |
| * Returns the JVM descriptor for the given annotation class, such as "Ljavax/inject/Qualifier;". |
| * |
| * @param clazz The annotation class |
| * @return JVM descriptor of the class |
| * @see ClassVisitor#visitAnnotation(String) |
| */ |
| public static String jvmDescriptor( final Class<? extends Annotation> clazz ) |
| { |
| return 'L' + clazz.getName().replace( '.', '/' ) + ';'; |
| } |
| |
| // ---------------------------------------------------------------------- |
| // Implementation methods |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * Adapts the given {@link ClassVisitor} to its equivalent ASM form. |
| * |
| * @param _cv The class visitor to adapt |
| * @return ASM-compatible class visitor |
| */ |
| private static org.eclipse.sisu.space.asm.ClassVisitor adapt( final ClassVisitor _cv ) |
| { |
| return null == _cv ? null : new org.eclipse.sisu.space.asm.ClassVisitor( Opcodes.ASM5 ) |
| { |
| @Override |
| public void visit( final int version, final int access, final String name, final String signature, |
| final String superName, final String[] interfaces ) |
| { |
| _cv.enterClass( access, name, superName, interfaces ); |
| } |
| |
| @Override |
| public org.eclipse.sisu.space.asm.AnnotationVisitor visitAnnotation( final String desc, |
| final boolean visible ) |
| { |
| final AnnotationVisitor _av = _cv.visitAnnotation( desc ); |
| return null == _av ? null : new org.eclipse.sisu.space.asm.AnnotationVisitor( Opcodes.ASM5 ) |
| { |
| { |
| _av.enterAnnotation(); |
| } |
| |
| @Override |
| public void visit( final String name, final Object value ) |
| { |
| _av.visitElement( name, value instanceof Type ? ( (Type) value ).getClassName() : value ); |
| } |
| |
| @Override |
| public void visitEnd() |
| { |
| _av.leaveAnnotation(); |
| } |
| }; |
| } |
| |
| @Override |
| public void visitEnd() |
| { |
| _cv.leaveClass(); |
| } |
| }; |
| } |
| } |