Analyzer.java

    1/*******************************************************************************
    2 * Copyright (c) 2009, 2010 Mountainminds GmbH & Co. KG and Contributors
    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 *******************************************************************************/
   12package org.jacoco.core.instr;
   13
   14import java.io.File;
   15import java.io.FileInputStream;
   16import java.io.IOException;
   17import java.io.InputStream;
   18import java.util.StringTokenizer;
   19import java.util.zip.ZipEntry;
   20import java.util.zip.ZipInputStream;
   21
   22import org.jacoco.core.data.IClassStructureVisitor;
   23import org.jacoco.core.data.IStructureVisitor;
   24import org.objectweb.asm.ClassReader;
   25import org.objectweb.asm.ClassVisitor;
   26
   27/**
   28 * Several APIs to analyze class structures.
   29 * 
   30 * @author Marc R. Hoffmann
   31 * @version 0.4.1.20101007204400
   32 */
   33public class Analyzer {
   34
   35    private final IStructureVisitor structureVisitor;
   36
   37    /**
   38     * Creates a new analyzer reporting to the given output.
   39     * 
   40     * @param structureVisitor
   41     *            the output instance that will receive all structure data
   42     */
   43    public Analyzer(final IStructureVisitor structureVisitor) {
   44        this.structureVisitor = structureVisitor;
   45    }
   46
   47    /**
   48     * Creates an ASM class visitor for analysis.
   49     * 
   50     * @param classid
   51     *            id of the class calculated with {@link CRC64}
   52     * @return ASM visitor to write class definition to
   53     */
   54    public ClassVisitor createAnalyzingVisitor(final long classid) {
   55        final IClassStructureVisitor classStructure = structureVisitor
   56                .visitClassStructure(classid);
   57        return new BlockClassAdapter(new ClassAnalyzer(classStructure));
   58    }
   59
   60    /**
   61     * Analyzes the class given as a ASM reader.
   62     * 
   63     * @param reader
   64     *            reader with class definitions
   65     */
   66    public void analyzeClass(final ClassReader reader) {
   67        final ClassVisitor visitor = createAnalyzingVisitor(CRC64
   68                .checksum(reader.b));
   69        reader.accept(visitor, 0);
   70    }
   71
   72    /**
   73     * Analyzes the class definition from a given in-memory buffer.
   74     * 
   75     * @param buffer
   76     *            class definitions
   77     */
   78    public void analyzeClass(final byte[] buffer) {
   79        analyzeClass(new ClassReader(buffer));
   80    }
   81
   82    /**
   83     * Analyzes the class definition from a given input stream.
   84     * 
   85     * @param input
   86     *            stream to read class definition from
   87     * @throws IOException
   88     */
   89    public void analyzeClass(final InputStream input) throws IOException {
   90        analyzeClass(new ClassReader(input));
   91    }
   92
   93    /**
   94     * Analyzes all classes contained in the ZIP archive (jar, war, ear, etc.)
   95     * given as an input stream. Contained archives are read recursively.
   96     * 
   97     * @param input
   98     *            ZIP archive data
   99     * @return number of class files found
  100     * @throws IOException
  101     */
  102    public int analyzeArchive(final InputStream input) throws IOException {
  103        final ZipInputStream zip = new ZipInputStream(input);
  104        int count = 0;
  105        while (true) {
  106            final ZipEntry entry = zip.getNextEntry();
  107            if (entry == null) {
  108                break;
  109            }
  110            count += analyzeAll(zip);
  111        }
  112        return count;
  113    }
  114
  115    /**
  116     * Analyzes all classes found in the given input stream. The input stream
  117     * may either represent a single class file or a ZIP archive that is
  118     * searched recursively for class files. All other content types are
  119     * ignored.
  120     * 
  121     * @param input
  122     *            input data
  123     * @return number of class files found
  124     * @throws IOException
  125     */
  126    public int analyzeAll(final InputStream input) throws IOException {
  127        final ContentTypeDetector detector = new ContentTypeDetector(input);
  128        switch (detector.getHeader()) {
  129        case ContentTypeDetector.CLASSFILE:
  130            analyzeClass(detector.getInputStream());
  131            return 1;
  132        case ContentTypeDetector.ZIPFILE:
  133            return analyzeArchive(detector.getInputStream());
  134        }
  135        return 0;
  136    }
  137
  138    /**
  139     * Analyzes all class files contained in the given file or folder. Class
  140     * files as well as ZIP files are considered. Folders are searched
  141     * recursively.
  142     * 
  143     * @param file
  144     *            file or folder to look for class files
  145     * @return number of class files found
  146     * @throws IOException
  147     */
  148    public int analyzeAll(final File file) throws IOException {
  149        int count = 0;
  150        if (file.isDirectory()) {
  151            for (final File f : file.listFiles()) {
  152                count += analyzeAll(f);
  153            }
  154        } else {
  155            final InputStream in = new FileInputStream(file);
  156            count += analyzeAll(in);
  157            in.close();
  158        }
  159        return count;
  160    }
  161
  162    /**
  163     * Analyzes all classes from the given class path. Directories containing
  164     * class files as well as archive files are considered.
  165     * 
  166     * @param path
  167     *            path definition
  168     * @param basedir
  169     *            optional base directory, if <code>null</code> the current
  170     *            working directory is used as the base for relative path
  171     *            entries
  172     * @return number of class files found
  173     * @throws IOException
  174     */
  175    public int analyzeAll(final String path, final File basedir)
  176            throws IOException {
  177        int count = 0;
  178        final StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
  179        while (st.hasMoreTokens()) {
  180            count += analyzeAll(new File(basedir, st.nextToken()));
  181        }
  182        return count;
  183    }
  184
  185}