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