Analyzer.java

    1/*******************************************************************************
    2 * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
    3 * All rights reserved. This program and the accompanying materials
    4 * are made available under the terms of the Eclipse Public License v1.0
    5 * which accompanies this distribution, and is available at
    6 * http://www.eclipse.org/legal/epl-v10.html
    7 *
    8 * Contributors:
    9 *    Marc R. Hoffmann - initial API and implementation
   10 *    
   11 * $Id: $
   12 *******************************************************************************/
   13package org.jacoco.core.instr;
   14
   15import static java.lang.String.format;
   16
   17import java.io.File;
   18import java.io.FileInputStream;
   19import java.io.IOException;
   20import java.io.InputStream;
   21import java.util.StringTokenizer;
   22import java.util.zip.ZipEntry;
   23import java.util.zip.ZipInputStream;
   24
   25import org.jacoco.core.data.IClassStructureVisitor;
   26import org.jacoco.core.data.IStructureVisitor;
   27import org.objectweb.asm.ClassReader;
   28import org.objectweb.asm.ClassVisitor;
   29import org.objectweb.asm.Opcodes;
   30
   31/**
   32 * Several APIs to analyze class structures.
   33 * 
   34 * @author Marc R. Hoffmann
   35 * @version $Revision: $
   36 */
   37public class Analyzer {
   38
   39    private final IStructureVisitor structureVisitor;
   40
   41    /**
   42     * Creates a new analyzer reporting to the given output.
   43     * 
   44     * @param structureVisitor
   45     *            the output instance that will receive all structure data
   46     */
   47    public Analyzer(final IStructureVisitor structureVisitor) {
   48        this.structureVisitor = structureVisitor;
   49    }
   50
   51    /**
   52     * Creates an ASM class visitor for analysis.
   53     * 
   54     * @param classid
   55     *            id of the class calculated with {@link CRC64}
   56     * @param classname
   57     *            VM name of the class
   58     * @return ASM visitor to write class definition to
   59     */
   60    public ClassVisitor createAnalyzingVisitor(final long classid,
   61            final String classname) {
   62        final IClassStructureVisitor classStructure = structureVisitor
   63                .visitClassStructure(classid, classname);
   64        return new ClassAnalyzer(classStructure);
   65    }
   66
   67    /**
   68     * Analyzes the class given as a ASM reader.
   69     * 
   70     * @param reader
   71     *            reader with class definitions
   72     */
   73    public void analyze(final ClassReader reader) {
   74        if ((reader.getAccess() & Opcodes.ACC_INTERFACE) != 0) {
   75            return;
   76        }
   77
   78        final ClassVisitor visitor = createAnalyzingVisitor(CRC64
   79                .checksum(reader.b), reader.getClassName());
   80        reader.accept(visitor, 0);
   81    }
   82
   83    /**
   84     * Analyzes the class definition from a given in-memory buffer.
   85     * 
   86     * @param buffer
   87     *            class definitions
   88     */
   89    public void analyze(final byte[] buffer) {
   90        analyze(new ClassReader(buffer));
   91    }
   92
   93    /**
   94     * Analyzes the class definition from a given input stream.
   95     * 
   96     * @param input
   97     *            stream to read class definition from
   98     * @throws IOException
   99     */
  100    public void analyze(final InputStream input) throws IOException {
  101        analyze(new ClassReader(input));
  102    }
  103
  104    /**
  105     * Analyzes the class definition contained in a given file.
  106     * 
  107     * @param file
  108     *            class file
  109     * @throws IOException
  110     */
  111    public void analyze(final File file) throws IOException {
  112        final InputStream in = new FileInputStream(file);
  113        analyze(new ClassReader(in));
  114        in.close();
  115    }
  116
  117    /**
  118     * Analyzes all class files contained in the given directory and its
  119     * children.
  120     * 
  121     * @param directory
  122     *            folder to look for class files
  123     * @throws IOException
  124     *             thrown if the given file object does not represent a readable
  125     *             directory
  126     */
  127    public void analyzeAll(final File directory) throws IOException {
  128        final File[] files = directory.listFiles();
  129        if (files == null) {
  130            throw new IOException(format("Can't read directory %s.", directory));
  131        }
  132        for (final File f : files) {
  133            if (f.isDirectory()) {
  134                analyzeAll(f);
  135                continue;
  136            }
  137            if (f.getName().endsWith(".class")) {
  138                analyze(f);
  139            }
  140        }
  141    }
  142
  143    /**
  144     * Analyzes all class files contained in a JAR file.
  145     * 
  146     * @param input
  147     *            stream to read the JAR file from
  148     * @throws IOException
  149     */
  150    public void analyzeJAR(final InputStream input) throws IOException {
  151        final ZipInputStream zip = new ZipInputStream(input);
  152        while (true) {
  153            final ZipEntry entry = zip.getNextEntry();
  154            if (entry == null) {
  155                break;
  156            }
  157            if (entry.getName().endsWith(".class")) {
  158                analyze(zip);
  159            }
  160        }
  161    }
  162
  163    /**
  164     * Analyzes all class files contained in a JAR file.
  165     * 
  166     * @param jarfile
  167     *            JAR file
  168     * @throws IOException
  169     */
  170    public void analyzeJAR(final File jarfile) throws IOException {
  171        final InputStream in = new FileInputStream(jarfile);
  172        analyzeJAR(in);
  173        in.close();
  174    }
  175
  176    /**
  177     * Analyzes all class from the given class path.
  178     * 
  179     * @param path
  180     *            path definition
  181     * @param basedir
  182     *            optional base directory, if <code>null</code> the current
  183     *            working directory is used as the base for relative path
  184     *            entries
  185     * @throws IOException
  186     */
  187    public void analyzePath(final String path, final File basedir)
  188            throws IOException {
  189        final StringTokenizer tokenizer = new StringTokenizer(path,
  190                File.pathSeparator);
  191        while (tokenizer.hasMoreTokens()) {
  192            final File entry = new File(basedir, tokenizer.nextToken());
  193            if (entry.isDirectory()) {
  194                analyzeAll(entry);
  195                continue;
  196            }
  197            if (entry.isFile() && entry.getName().endsWith(".jar")) {
  198                analyzeJAR(entry);
  199            }
  200        }
  201    }
  202
  203}