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     * @return ASM visitor to write class definition to
   57     */
   58    public ClassVisitor createAnalyzingVisitor(final long classid) {
   59        final IClassStructureVisitor classStructure = structureVisitor
   60                .visitClassStructure(classid);
   61        return new ClassAnalyzer(classStructure);
   62    }
   63
   64    /**
   65     * Analyzes the class given as a ASM reader.
   66     * 
   67     * @param reader
   68     *            reader with class definitions
   69     */
   70    public void analyze(final ClassReader reader) {
   71        if ((reader.getAccess() & Opcodes.ACC_INTERFACE) != 0) {
   72            return;
   73        }
   74
   75        final ClassVisitor visitor = createAnalyzingVisitor(CRC64
   76                .checksum(reader.b));
   77        reader.accept(visitor, 0);
   78    }
   79
   80    /**
   81     * Analyzes the class definition from a given in-memory buffer.
   82     * 
   83     * @param buffer
   84     *            class definitions
   85     */
   86    public void analyze(final byte[] buffer) {
   87        analyze(new ClassReader(buffer));
   88    }
   89
   90    /**
   91     * Analyzes the class definition from a given input stream.
   92     * 
   93     * @param input
   94     *            stream to read class definition from
   95     * @throws IOException
   96     */
   97    public void analyze(final InputStream input) throws IOException {
   98        analyze(new ClassReader(input));
   99    }
  100
  101    /**
  102     * Analyzes the class definition contained in a given file.
  103     * 
  104     * @param file
  105     *            class file
  106     * @throws IOException
  107     */
  108    public void analyze(final File file) throws IOException {
  109        final InputStream in = new FileInputStream(file);
  110        analyze(new ClassReader(in));
  111        in.close();
  112    }
  113
  114    /**
  115     * Analyzes all class files contained in the given directory and its
  116     * children.
  117     * 
  118     * @param directory
  119     *            folder to look for class files
  120     * @throws IOException
  121     *             thrown if the given file object does not represent a readable
  122     *             directory
  123     */
  124    public void analyzeAll(final File directory) throws IOException {
  125        final File[] files = directory.listFiles();
  126        if (files == null) {
  127            throw new IOException(format("Can't read directory %s.", directory));
  128        }
  129        for (final File f : files) {
  130            if (f.isDirectory()) {
  131                analyzeAll(f);
  132                continue;
  133            }
  134            if (f.getName().endsWith(".class")) {
  135                analyze(f);
  136            }
  137        }
  138    }
  139
  140    /**
  141     * Analyzes all class files contained in a JAR file.
  142     * 
  143     * @param input
  144     *            stream to read the JAR file from
  145     * @throws IOException
  146     */
  147    public void analyzeJAR(final InputStream input) throws IOException {
  148        final ZipInputStream zip = new ZipInputStream(input);
  149        while (true) {
  150            final ZipEntry entry = zip.getNextEntry();
  151            if (entry == null) {
  152                break;
  153            }
  154            if (entry.getName().endsWith(".class")) {
  155                analyze(zip);
  156            }
  157        }
  158    }
  159
  160    /**
  161     * Analyzes all class files contained in a JAR file.
  162     * 
  163     * @param jarfile
  164     *            JAR file
  165     * @throws IOException
  166     */
  167    public void analyzeJAR(final File jarfile) throws IOException {
  168        final InputStream in = new FileInputStream(jarfile);
  169        analyzeJAR(in);
  170        in.close();
  171    }
  172
  173    /**
  174     * Analyzes all class from the given class path.
  175     * 
  176     * @param path
  177     *            path definition
  178     * @param basedir
  179     *            optional base directory, if <code>null</code> the current
  180     *            working directory is used as the base for relative path
  181     *            entries
  182     * @throws IOException
  183     */
  184    public void analyzePath(final String path, final File basedir)
  185            throws IOException {
  186        final StringTokenizer tokenizer = new StringTokenizer(path,
  187                File.pathSeparator);
  188        while (tokenizer.hasMoreTokens()) {
  189            final File entry = new File(basedir, tokenizer.nextToken());
  190            if (entry.isDirectory()) {
  191                analyzeAll(entry);
  192                continue;
  193            }
  194            if (entry.isFile() && entry.getName().endsWith(".jar")) {
  195                analyzeJAR(entry);
  196            }
  197        }
  198    }
  199
  200}