CoverageBuilder.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.analysis;
   13
   14import java.util.ArrayList;
   15import java.util.Collection;
   16import java.util.Collections;
   17import java.util.HashMap;
   18import java.util.Map;
   19
   20import org.jacoco.core.data.ExecutionData;
   21import org.jacoco.core.data.ExecutionDataStore;
   22import org.jacoco.core.data.IClassStructureVisitor;
   23import org.jacoco.core.data.IMethodStructureVisitor;
   24import org.jacoco.core.data.IStructureVisitor;
   25
   26/**
   27 * Builder for hierarchical {@link ICoverageNode} structures based on execution
   28 * and structure information. The builder is constructed for a given
   29 * {@link ExecutionDataStore} and then feed with class structure information
   30 * through its {@link IStructureVisitor} interface. Afterwards the collected
   31 * data can be obtained with {@link #getClasses()}, {@link #getSourceFiles()} or
   32 * {@link #getBundle(String)}.
   33 * 
   34 * @author Marc R. Hoffmann
   35 * @version 0.4.1.20101007204400
   36 */
   37public class CoverageBuilder implements IStructureVisitor {
   38
   39    private final ExecutionDataStore executionData;
   40
   41    private final StringPool stringPool;
   42
   43    private final Map<Long, ClassCoverage> classes;
   44
   45    private final Map<String, SourceFileCoverage> sourcefiles;
   46
   47    /**
   48     * Create a new builder based on the given execution data.
   49     * 
   50     * @param executionData
   51     *            execution data
   52     */
   53    public CoverageBuilder(final ExecutionDataStore executionData) {
   54        this(executionData, new StringPool());
   55    }
   56
   57    /**
   58     * Create a new builder based on the given execution data.
   59     * 
   60     * @param executionData
   61     *            execution data
   62     * @param stringPool
   63     *            pool to optimize the number of {@link String} instances
   64     */
   65    public CoverageBuilder(final ExecutionDataStore executionData,
   66            final StringPool stringPool) {
   67        this.executionData = executionData;
   68        this.stringPool = stringPool;
   69        this.classes = new HashMap<Long, ClassCoverage>();
   70        this.sourcefiles = new HashMap<String, SourceFileCoverage>();
   71    }
   72
   73    /**
   74     * Returns all class nodes currently contained in this builder.
   75     * 
   76     * @return all class nodes
   77     */
   78    public Collection<ClassCoverage> getClasses() {
   79        return Collections.unmodifiableCollection(classes.values());
   80    }
   81
   82    /**
   83     * Returns all source file nodes currently contained in this builder.
   84     * 
   85     * @return all source file nodes
   86     */
   87    public Collection<SourceFileCoverage> getSourceFiles() {
   88        return Collections.unmodifiableCollection(sourcefiles.values());
   89    }
   90
   91    /**
   92     * Creates a bundle from all nodes currently contained in this bundle.
   93     * 
   94     * @param name
   95     *            Name of the bundle
   96     * @return bundle containing all classes and source files
   97     */
   98    public BundleCoverage getBundle(final String name) {
   99        return new BundleCoverage(name, classes.values(), sourcefiles.values(),
  100                stringPool);
  101    }
  102
  103    // === IStructureVisitor ===
  104
  105    public IClassStructureVisitor visitClassStructure(final long id) {
  106        final ExecutionData data = executionData.get(id);
  107        final boolean[] covered = data == null ? null : data.getData();
  108        final Collection<MethodCoverage> methods = new ArrayList<MethodCoverage>();
  109        return new IClassStructureVisitor() {
  110            String name;
  111            String signature;
  112            String superName;
  113            String[] interfaces;
  114            String sourcename;
  115
  116            public void visit(final String name, final String signature,
  117                    final String superName, final String[] interfaces) {
  118                this.name = stringPool.get(name);
  119                this.signature = stringPool.get(signature);
  120                this.superName = stringPool.get(superName);
  121                this.interfaces = stringPool.get(interfaces);
  122            }
  123
  124            public void visitSourceFile(final String name) {
  125                sourcename = stringPool.get(name);
  126            }
  127
  128            public IMethodStructureVisitor visitMethodStructure(
  129                    final String name, final String desc, final String signature) {
  130                return createMethodVisitor(name, desc, signature, methods,
  131                        covered);
  132            }
  133
  134            public void visitEnd() {
  135                final ClassCoverage classData = new ClassCoverage(name, id,
  136                        signature, superName, interfaces, sourcename, methods);
  137                // Only consider classes that actually contain code:
  138                if (classData.getInstructionCounter().getTotalCount() > 0) {
  139                    classes.put(Long.valueOf(id), classData);
  140                    if (sourcename != null) {
  141                        final String packageName = stringPool.get(classData
  142                                .getPackageName());
  143                        final SourceFileCoverage sourceFile = getSourceFile(
  144                                sourcename, packageName);
  145                        sourceFile.increment(classData);
  146                    }
  147                }
  148            }
  149        };
  150    }
  151
  152    private IMethodStructureVisitor createMethodVisitor(final String name,
  153            final String desc, final String signature,
  154            final Collection<MethodCoverage> container, final boolean[] covered) {
  155        final MethodCoverage method = new MethodCoverage(stringPool.get(name),
  156                stringPool.get(desc), stringPool.get(signature));
  157        return new IMethodStructureVisitor() {
  158            public void block(final int id, final int instructions,
  159                    final int[] lineNumbers) {
  160                final boolean c = covered == null ? false : covered[id];
  161                method.addBlock(instructions, lineNumbers, c);
  162            }
  163
  164            public void visitEnd() {
  165                // Only consider methods that actually contain code:
  166                if (method.getInstructionCounter().getTotalCount() > 0) {
  167                    container.add(method);
  168                }
  169            }
  170        };
  171    }
  172
  173    private SourceFileCoverage getSourceFile(final String filename,
  174            final String packagename) {
  175        final String key = packagename + '/' + filename;
  176        SourceFileCoverage sourcefile = sourcefiles.get(key);
  177        if (sourcefile == null) {
  178            sourcefile = new SourceFileCoverage(filename, packagename);
  179            sourcefiles.put(key, sourcefile);
  180        }
  181        return sourcefile;
  182    }
  183
  184}