JaCoCo initial drop.
diff --git a/org.jacoco.core/.classpath b/org.jacoco.core/.classpath
new file mode 100644
index 0000000..2d1a430
--- /dev/null
+++ b/org.jacoco.core/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.jacoco.core/.project b/org.jacoco.core/.project
new file mode 100644
index 0000000..70a5780
--- /dev/null
+++ b/org.jacoco.core/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.jacoco.core</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.jacoco.core/.settings/org.eclipse.jdt.core.prefs b/org.jacoco.core/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..a56f637
--- /dev/null
+++ b/org.jacoco.core/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,78 @@
+#Mon May 18 07:55:32 CEST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=no_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/org.jacoco.core/.settings/org.eclipse.jdt.ui.prefs b/org.jacoco.core/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..cb85ad6
--- /dev/null
+++ b/org.jacoco.core/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,56 @@
+#Fri May 29 18:31:30 CEST 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_EclEmma
+formatter_settings_version=11
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=true
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=true
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.jacoco.core/META-INF/MANIFEST.MF b/org.jacoco.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..51f6999
--- /dev/null
+++ b/org.jacoco.core/META-INF/MANIFEST.MF
@@ -0,0 +1,13 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: JaCoCo Core
+Bundle-SymbolicName: org.jacoco.core
+Bundle-Version: 0.1.0.qualifier
+Bundle-Vendor: Mountainminds GmbH & Co. KG
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: org.objectweb.asm,
+ org.objectweb.asm.commons,
+ org.objectweb.asm.tree
+Export-Package: org.jacoco.core.data,
+ org.jacoco.core.instr,
+ org.jacoco.core.runtime
diff --git a/org.jacoco.core/about.html b/org.jacoco.core/about.html
new file mode 100644
index 0000000..f5d8b33
--- /dev/null
+++ b/org.jacoco.core/about.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<html>
+<head>
+<title>About</title>
+<meta http-equiv=Content-Type content="text/html; charset=ISO-8859-1">
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>
+ JaCoCo is a free Java code coverage library implemented by Marc R. Hoffmann.
+ Check <a href="http://www.eclemma.org/devdoc/jacoco.html">http://www.eclemma.org/devdoc/jacoco.html</a>
+ for updates, documentation and support.
+</p>
+
+<h3>License</h3>
+
+<p>
+ The Content in this plug-in is made available by Mountainminds GmbH & Co. KG,
+ Munich. Except indicated below, the Content is provided to you under the terms
+ and conditions of the Eclipse Public License Version 1.0 ("EPL").
+ A copy of the EPL is available at
+ <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+</p>
+
+<h3>Third-party Licenses</h3>
+
+<p>
+ This bundle is based on the <a href="http://asm.objectweb.org/">ASM</a>
+ library which is distributed under the following license.
+</p>
+
+<pre>
+Copyright (c) 2000-2005 INRIA, France Telecom
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holders nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+</pre>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/org.jacoco.core/build.properties b/org.jacoco.core/build.properties
new file mode 100644
index 0000000..a657887
--- /dev/null
+++ b/org.jacoco.core/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ about.html
diff --git a/org.jacoco.core/buildhook.xml b/org.jacoco.core/buildhook.xml
new file mode 100644
index 0000000..0cbbe07
--- /dev/null
+++ b/org.jacoco.core/buildhook.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="org.jacoco.core.buildhook">
+
+ <path id="dependencies">
+ <path refid="bundle-org.objectweb.asm"/>
+ </path>
+
+ <property name="do.compile" value="true"/>
+ <property name="do.package" value="true"/>
+ <property name="do.doc" value="true"/>
+
+</project>
\ No newline at end of file
diff --git a/org.jacoco.core/src/org/jacoco/core/data/AbstractCounter.java b/org.jacoco.core/src/org/jacoco/core/data/AbstractCounter.java
new file mode 100644
index 0000000..f6eae89
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/AbstractCounter.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+/**
+ * Base class for {@link ICounter} implementations.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public abstract class AbstractCounter implements ICounter {
+
+ /** total number of items */
+ protected int total;
+
+ /** covered number of items */
+ protected int covered;
+
+ /**
+ * Creates a instance with the given numbers.
+ *
+ * @param total
+ * number of total items
+ * @param covered
+ * number of covered items
+ */
+ protected AbstractCounter(final int total, final int covered) {
+ this.total = total;
+ this.covered = covered;
+ }
+
+ public int getTotalCount() {
+ return total;
+ }
+
+ public int getCoveredCount() {
+ return covered;
+ }
+
+ public double getRatio() {
+ return (double) covered / total;
+ }
+
+ public int compareTo(final ICounter counter) {
+ return Double.compare(getRatio(), counter.getRatio());
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj instanceof ICounter) {
+ final ICounter that = (ICounter) obj;
+ return this.total == that.getTotalCount()
+ && this.covered == that.getCoveredCount();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return total ^ covered * 17;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer b = new StringBuffer("Counter["); //$NON-NLS-1$
+ b.append(getCoveredCount());
+ b.append('/').append(getTotalCount());
+ b.append(']');
+ return b.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/org.jacoco.core/src/org/jacoco/core/data/BlockCoverageData.java b/org.jacoco.core/src/org/jacoco/core/data/BlockCoverageData.java
new file mode 100644
index 0000000..9390ec4
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/BlockCoverageData.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+/**
+ * Coverage data of a single basic block.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class BlockCoverageData extends CoverageDataImpl {
+
+ /**
+ * Creates a new block data object with the given parameters.
+ *
+ * @param instructionCount
+ * number of byte code instructions contained in this block
+ * @param lineNumbers
+ * source lines corresponding to this block
+ * @param covered
+ * <code>true</code>, if this block is covered
+ */
+ public BlockCoverageData(final int instructionCount,
+ final int[] lineNumbers, final boolean covered) {
+ super(ElementType.BLOCK, true);
+ blockCounter = CounterImpl.getInstance(covered);
+ instructionCounter = CounterImpl.getInstance(instructionCount, covered);
+ lines.increment(lineNumbers, covered);
+ }
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/ClassCoverageData.java b/org.jacoco.core/src/org/jacoco/core/data/ClassCoverageData.java
new file mode 100644
index 0000000..fae0e0f
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/ClassCoverageData.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+import java.util.Collection;
+
+/**
+ * Coverage data of a single class.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class ClassCoverageData extends CoverageDataImpl {
+
+ private final String name;
+
+ private final String bundle;
+
+ /**
+ * Creates a class coverage data object with the given parameters.
+ *
+ * @param name
+ * vm name of the class
+ * @param bundle
+ * optional bundle oder <code>null</code>
+ * @param methods
+ * contained methods
+ */
+ public ClassCoverageData(final String name, final String bundle,
+ final Collection<ICoverageData> methods) {
+ super(ElementType.CLASS, true);
+ this.name = name;
+ this.bundle = bundle;
+ addAll(methods);
+ // As class is considered as covered when at least one method is
+ // covered:
+ final boolean covered = methodCounter.getCoveredCount() > 0;
+ this.classCounter = CounterImpl.getInstance(covered);
+ }
+
+ /**
+ * Return the vm name of this class.
+ *
+ * @return vm name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the vm name of the package this class belongs to.
+ *
+ * @return vm name of the package
+ */
+ public String getPackagename() {
+ final int pos = name.lastIndexOf('/');
+ return pos == -1 ? "" : name.substring(0, pos);
+ }
+
+ /**
+ * Returns the optional bundle identifier for this class.
+ *
+ * @return bundle or <code>null</code>
+ */
+ public String getBundle() {
+ return bundle;
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/CounterImpl.java b/org.jacoco.core/src/org/jacoco/core/data/CounterImpl.java
new file mode 100644
index 0000000..57ed6a9
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/CounterImpl.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+/**
+ * {@link ICounter} implementations. Implementing a factory pattern allows to
+ * share counter instances.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public abstract class CounterImpl extends AbstractCounter {
+
+ /** Max counter value for which singletons are created */
+ private static final int SINGLETON_LIMIT = 10;
+
+ private static final CounterImpl[][] SINGLETONS = new CounterImpl[SINGLETON_LIMIT + 1][];
+
+ static {
+ for (int i = 0; i <= SINGLETON_LIMIT; i++) {
+ SINGLETONS[i] = new CounterImpl[i + 1];
+ for (int j = 0; j <= i; j++) {
+ SINGLETONS[i][j] = new Fix(i, j);
+ }
+ }
+ }
+
+ /** Constant for Counter with 0/0 values. */
+ public static final CounterImpl COUNTER_0_0 = SINGLETONS[0][0];
+
+ /**
+ * Mutable version of the counter.
+ */
+ private static class Var extends CounterImpl {
+ public Var(final int total, final int covered) {
+ super(total, covered);
+ }
+
+ @Override
+ public CounterImpl increment(final ICounter counter) {
+ this.total += counter.getTotalCount();
+ this.covered += counter.getCoveredCount();
+ return this;
+ }
+ }
+
+ /**
+ * Immutable version of the counter.
+ */
+ private static class Fix extends CounterImpl {
+ public Fix(final int total, final int covered) {
+ super(total, covered);
+ }
+
+ @Override
+ public CounterImpl increment(final ICounter counter) {
+ return getInstance(this.total + counter.getTotalCount(),
+ this.covered + counter.getCoveredCount());
+ }
+ }
+
+ /**
+ * Factory method to retrieve a counter with the given number of items.
+ *
+ * @param total
+ * total number of items
+ * @param covered
+ * covered number of items
+ * @return counter instance
+ */
+ public static CounterImpl getInstance(final int total, final int covered) {
+ if (total <= SINGLETON_LIMIT && covered <= total) {
+ return SINGLETONS[total][covered];
+ } else {
+ return new Var(total, covered);
+ }
+ }
+
+ /**
+ * Factory method to retrieve a counter with the given number of items.
+ *
+ * @param total
+ * total number of items
+ * @param covered
+ * <code>true</code>, if all items are covered
+ * @return counter instance
+ */
+ public static CounterImpl getInstance(final int total, final boolean covered) {
+ return getInstance(total, covered ? total : 0);
+ }
+
+ /**
+ * Factory method to retrieve a counter for a single item.
+ *
+ * @param covered
+ * <code>true</code>, if the item is covered
+ * @return counter instance
+ */
+ public static CounterImpl getInstance(final boolean covered) {
+ return getInstance(1, covered ? 1 : 0);
+ }
+
+ /**
+ * Creates a new instance with the given figures.
+ *
+ * @param total
+ * total number of items
+ * @param covered
+ * covered number of items
+ */
+ protected CounterImpl(final int total, final int covered) {
+ super(total, covered);
+ }
+
+ /**
+ * Returns a counter with values incremented by the numbers of the given
+ * counter. It is up to the implementation whether this counter instance is
+ * modified or a new instance is returned.
+ *
+ * @param counter
+ * number of additional total and covered items
+ * @return counter instance with incremented values
+ */
+ public abstract CounterImpl increment(final ICounter counter);
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/CoverageDataBuilder.java b/org.jacoco.core/src/org/jacoco/core/data/CoverageDataBuilder.java
new file mode 100644
index 0000000..6c23826
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/CoverageDataBuilder.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Builder for hierarchical {@link ICoverageData} structures based on execution
+ * and structure information. The builder is constructed for a given
+ * {@link ExecutionDataStore} and then feed with class structure information
+ * through its {@link IStructureOutput} interface.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class CoverageDataBuilder implements IStructureOutput {
+
+ private final ExecutionDataStore executionData;
+
+ private final Map<Long, ClassCoverageData> classes;
+
+ /**
+ * Create a new builder based on the given execution data.
+ *
+ * @param executionData
+ * execution data
+ */
+ public CoverageDataBuilder(final ExecutionDataStore executionData) {
+ this.executionData = executionData;
+ this.classes = new HashMap<Long, ClassCoverageData>();
+ }
+
+ public IClassStructureOutput classStructure(final long id,
+ final String name, final String bundle) {
+ final boolean[][] covered = executionData.getBlockdata(id);
+ final Collection<ICoverageData> methods = new ArrayList<ICoverageData>();
+ final String[] sourcename = new String[1];
+ return new IClassStructureOutput() {
+ public void sourceFile(final String name) {
+ sourcename[0] = name;
+ }
+
+ public IMethodStructureOutput methodStructure(final int id,
+ final String name, final String desc, final String signature) {
+ final boolean[] c = covered == null ? null : covered[id];
+ return createMethodOutput(name, desc, signature, methods, c);
+ }
+
+ public void end() {
+ final ClassCoverageData classData = new ClassCoverageData(name,
+ bundle, methods);
+ classes.put(Long.valueOf(id), classData);
+ if (sourcename[0] != null) {
+ final SourceFileCoverageData sourceFile = getSourceFile(
+ sourcename[0], classData.getPackagename(), bundle);
+ sourceFile.add(classData);
+ }
+ }
+ };
+ }
+
+ private IMethodStructureOutput createMethodOutput(final String name,
+ final String desc, final String signature,
+ final Collection<ICoverageData> container, final boolean[] covered) {
+ final Collection<ICoverageData> blocks = new ArrayList<ICoverageData>();
+ return new IMethodStructureOutput() {
+ public void block(final int id, final int instructionCount,
+ final int[] lineNumbers) {
+ final boolean c = covered == null ? false : covered[id];
+ blocks.add(new BlockCoverageData(instructionCount, lineNumbers,
+ c));
+ }
+
+ public void end() {
+ container.add(new MethodCoverageData(name, desc, signature,
+ blocks));
+ }
+ };
+ }
+
+ private SourceFileCoverageData getSourceFile(final String filename,
+ final String packagename, final String bundle) {
+ // TODO look for existing file.
+ return new SourceFileCoverageData(filename, packagename, bundle);
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/CoverageDataImpl.java b/org.jacoco.core/src/org/jacoco/core/data/CoverageDataImpl.java
new file mode 100644
index 0000000..05e23d8
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/CoverageDataImpl.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Base implementation for coverage data nodes.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class CoverageDataImpl implements ICoverageData {
+
+ private final ElementType elementType;
+
+ private final Collection<ICoverageData> children;
+
+ /** Counter for blocks. */
+ protected CounterImpl blockCounter;
+
+ /** Counter for instructions. */
+ protected CounterImpl instructionCounter;
+
+ /** Counter for lines, if this element does not have lines. */
+ protected CounterImpl lineCounter;
+
+ /** Line information if this element has lines. */
+ protected final LinesImpl lines;
+
+ /** Counter for methods. */
+ protected CounterImpl methodCounter;
+
+ /** Counter for classes. */
+ protected CounterImpl classCounter;
+
+ /**
+ * Creates a new coverage data instance of the given element type.
+ *
+ * @param elementType
+ * type of the element represented by this instance
+ * @param hasLines
+ * <code>true</code> id this element has source lines
+ */
+ public CoverageDataImpl(final ElementType elementType,
+ final boolean hasLines) {
+ this.elementType = elementType;
+ children = new ArrayList<ICoverageData>();
+ blockCounter = CounterImpl.COUNTER_0_0;
+ instructionCounter = CounterImpl.COUNTER_0_0;
+ if (hasLines) {
+ lineCounter = null;
+ lines = new LinesImpl();
+ } else {
+ lineCounter = CounterImpl.COUNTER_0_0;
+ lines = null;
+ }
+ methodCounter = CounterImpl.COUNTER_0_0;
+ classCounter = CounterImpl.COUNTER_0_0;
+
+ }
+
+ /**
+ * Adds the given coverage data instance as a child element. All counters
+ * are incremented by the values of the given child.
+ *
+ * @param child
+ * child element to add
+ */
+ public void add(final ICoverageData child) {
+ children.add(child);
+ blockCounter = blockCounter.increment(child.getBlockCounter());
+ instructionCounter = instructionCounter.increment(child
+ .getInstructionCounter());
+ if (lineCounter != null) {
+ lineCounter = lineCounter.increment(child.getLineCounter());
+ }
+ if (lines != null) {
+ lines.increment(child.getLines());
+ }
+ methodCounter = methodCounter.increment(child.getMethodCounter());
+ classCounter = classCounter.increment(child.getClassCounter());
+ }
+
+ /**
+ * Adds the given collection of coverage data instances as child elements.
+ * All counters are incremented by the values of the given children.
+ *
+ * @param children
+ * child elements to add
+ */
+ public void addAll(final Collection<ICoverageData> children) {
+ for (final ICoverageData child : children) {
+ add(child);
+ }
+ }
+
+ // === ICoverageData ===
+
+ public ElementType getElementType() {
+ return elementType;
+ }
+
+ public Collection<ICoverageData> getChilden() {
+ return children;
+ }
+
+ public ICounter getBlockCounter() {
+ return blockCounter;
+ }
+
+ public ICounter getInstructionCounter() {
+ return instructionCounter;
+ }
+
+ public ICounter getLineCounter() {
+ return lines == null ? lineCounter : lines;
+ }
+
+ public ILines getLines() {
+ return lines;
+ }
+
+ public ICounter getMethodCounter() {
+ return methodCounter;
+ }
+
+ public ICounter getClassCounter() {
+ return classCounter;
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataDumper.java b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataDumper.java
new file mode 100644
index 0000000..9ddb5f4
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataDumper.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+import java.io.PrintStream;
+
+/**
+ * {@link IExecutionDataOutput} implementation that dumps a textual
+ * representation of coverage data.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class ExecutionDataDumper implements IExecutionDataOutput {
+
+ private final PrintStream out;
+
+ /**
+ * Dumps coverage data to {@link System#out}.
+ */
+ public ExecutionDataDumper() {
+ this(System.out);
+ }
+
+ /**
+ * Dumps coverage data to the given stream.
+ *
+ * @param out
+ * dump output
+ */
+ public ExecutionDataDumper(final PrintStream out) {
+ this.out = out;
+ }
+
+ public void classExecution(final long id, final boolean[][] blockdata) {
+ out.print("class coverage ");
+ out.print(" id=");
+ out.println(Long.toHexString(id));
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java
new file mode 100644
index 0000000..6f3faa6
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * In-memory data store for execution data. The data can be added through its
+ * {@link IExecutionDataOutput} interface. If execution data is provided
+ * multiple times for the same class the data is merged, i.e. a block is marked
+ * as executed if it is reported as executed at least once. This allows to merge
+ * coverage date from multiple runs. This class is not thread safe.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class ExecutionDataStore implements IExecutionDataOutput {
+
+ private final Map<Long, boolean[][]> data = new HashMap<Long, boolean[][]>();
+
+ public void classExecution(long classid, boolean[][] blockdata) {
+ boolean[][] current = data.get(classid);
+ if (current != null) {
+ merge(current, blockdata);
+ blockdata = current;
+ }
+ data.put(Long.valueOf(classid), blockdata);
+
+ }
+
+ private static void merge(boolean[][] target, boolean[][] data) {
+ if (target.length != data.length) {
+ throw new IllegalStateException("Incompatible execution data.");
+ }
+ for (int i = 0; i < target.length; i++) {
+ merge(target[i], data[i]);
+ }
+ }
+
+ private static void merge(boolean[] target, boolean[] data) {
+ if (target.length != data.length) {
+ throw new IllegalStateException("Incompatible execution data.");
+ }
+ for (int i = 0; i < target.length; i++) {
+ if (!target[i]) {
+ target[i] = data[i];
+ }
+ }
+ }
+
+ /**
+ * Returns the coverage data for the class with the given identifier if
+ * available.
+ *
+ * @param classid
+ * class identifier
+ * @return coverage data or <code>null</code>
+ */
+ public boolean[][] getBlockdata(long classid) {
+ return data.get(classid);
+ }
+
+ /**
+ * Writes the content of the store to the given output interface.
+ *
+ * @param output
+ * interface to write content to
+ */
+ public void writeTo(IExecutionDataOutput output) {
+ for (final Map.Entry<Long, boolean[][]> entry : data.entrySet()) {
+ output.classExecution(entry.getKey(), entry.getValue());
+ }
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/IClassStructureOutput.java b/org.jacoco.core/src/org/jacoco/core/data/IClassStructureOutput.java
new file mode 100644
index 0000000..9474924
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/IClassStructureOutput.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+/**
+ * Interface for data output of the internal structure of a single class. This
+ * interface is meant to be implemented by parties that want to retrieve data
+ * from the instrumentation process.
+ */
+public interface IClassStructureOutput {
+
+ /**
+ * The source file name might be reported through this method call.
+ *
+ * @param name
+ * name of the corresponding source file
+ */
+ public void sourceFile(String name);
+
+ /**
+ * Called for every instrumented method.
+ *
+ * @param id
+ * identifier of this method within the class
+ * @param name
+ * name of the method
+ * @param desc
+ * parameter and return value description
+ * @param signature
+ * generic signature or <code>null</code>
+ * @return call-back for structure details about the method
+ */
+ public IMethodStructureOutput methodStructure(int id, String name,
+ String desc, String signature);
+
+ /**
+ * Called after all information for this class has been emitted.
+ */
+ public void end();
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/ICounter.java b/org.jacoco.core/src/org/jacoco/core/data/ICounter.java
new file mode 100644
index 0000000..5bca9ca
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/ICounter.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+/**
+ * A counter holds the total and the covered number of particular items like
+ * classes, methods, blocks or instructions. Counters provide canonical
+ * comparison of their covered/total ratios.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: 11 $
+ */
+public interface ICounter extends Comparable<ICounter> {
+
+ /**
+ * Returns the total count number of instrumented items.
+ *
+ * @return total count of instrumented items
+ */
+ public int getTotalCount();
+
+ /**
+ * Returns the total count number of covered items.
+ *
+ * @return total count of covered items
+ */
+ public int getCoveredCount();
+
+ /**
+ * Calculates the ratio of covered to total count items. If total count
+ * items is 0 this method returns NaN.
+ *
+ * @return ratio of covered to total count items
+ */
+ public double getRatio();
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/ICoverageData.java b/org.jacoco.core/src/org/jacoco/core/data/ICoverageData.java
new file mode 100644
index 0000000..c22413a
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/ICoverageData.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+import java.util.Collection;
+
+/**
+ * Common interface for all data elements storing coverage data.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: 174 $
+ */
+public interface ICoverageData {
+
+ /**
+ * Type of a element represented by a {@link ICoverageData} instance.
+ */
+ public enum ElementType {
+
+ /** Basic Block */
+ BLOCK,
+
+ /** Method */
+ METHOD,
+
+ /** Class */
+ CLASS,
+
+ /** Source File */
+ SOURCEFILE,
+
+ /** Java Package */
+ PACKAGE,
+
+ /** Custom compilation */
+ CUSTOM
+
+ }
+
+ /**
+ * Returns the type of element represented by this node.
+ *
+ * @return type of this node
+ */
+ public ElementType getElementType();
+
+ /**
+ * Returns the child elements contained in this node.
+ *
+ * @return child elements
+ */
+ public Collection<ICoverageData> getChilden();
+
+ /**
+ * Returns the counter for byte code instructions.
+ *
+ * @return counter for instructions
+ */
+ public ICounter getInstructionCounter();
+
+ /**
+ * Returns the counter for blocks.
+ *
+ * @return counter for blocks
+ */
+ public ICounter getBlockCounter();
+
+ /**
+ * Returns the counter for lines.
+ *
+ * @return counter for lines
+ */
+ public ICounter getLineCounter();
+
+ /**
+ * Returns the line coverage information if this element supports it.
+ *
+ * @return line coverage or <code>null</code>
+ */
+ public ILines getLines();
+
+ /**
+ * Returns the counter for methods.
+ *
+ * @return counter for methods
+ */
+ public ICounter getMethodCounter();
+
+ /**
+ * Returns the counter for types.
+ *
+ * @return counter for types
+ */
+ public ICounter getClassCounter();
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/IExecutionDataOutput.java b/org.jacoco.core/src/org/jacoco/core/data/IExecutionDataOutput.java
new file mode 100644
index 0000000..5fcd3bb
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/IExecutionDataOutput.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+/**
+ * Interface for data output of collected execution data. This interface is
+ * meant to be implemented by parties that want to retrieve data from the
+ * coverage runtime.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public interface IExecutionDataOutput {
+
+ /**
+ * Provides execution data for the class with the given id. The first
+ * dimension of the array corresponds to the method id the second to the
+ * block id as provided with the structure data for this class. A value of
+ * <code>true</code> indicates that a block has been executed, i.e. its last
+ * instruction was called.
+ *
+ * @param id
+ * id of the instrumented class
+ * @param blockdata
+ * coverage data for the class
+ */
+ public void classExecution(long id, boolean[][] blockdata);
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/ILines.java b/org.jacoco.core/src/org/jacoco/core/data/ILines.java
new file mode 100644
index 0000000..eb744c1
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/ILines.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+/**
+ * For all elements that are located in a source file and for source files
+ * itself individual line coverage is described by this interface.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: 11 $
+ */
+public interface ILines extends ICounter {
+
+ /** Flag for lines that do not contain code (value is 0x00). */
+ public static final byte NO_CODE = 0x00;
+
+ /** Flag for lines that are not covered (value is 0x01). */
+ public static final byte NOT_COVERED = 0x01;
+
+ /** Flag for lines that are fully covered (value is 0x02). */
+ public static final byte FULLY_COVERED = 0x02;
+
+ /** Flag for lines that are partly covered (value is 0x03). */
+ public static final byte PARTLY_COVERED = NOT_COVERED | FULLY_COVERED;
+
+ /**
+ * The number of the first line coverage information is available for. If no
+ * line is yet contained, the method returns -1.
+ *
+ * @return number of the first line or -1
+ */
+ public int getFirstLine();
+
+ /**
+ * The number of the last line coverage information is available for. If no
+ * line is yet contained, the method returns -1.
+ *
+ * @return number of the last line or -1
+ */
+ public int getLastLine();
+
+ /**
+ * Returns the coverage status of the given line.
+ *
+ * @see #NO_CODE
+ * @see #NOT_COVERED
+ * @see #PARTLY_COVERED
+ * @see #FULLY_COVERED
+ *
+ * @param line
+ * number of the line in question
+ * @return status of this line
+ */
+ public byte getStatus(int line);
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/IMethodStructureOutput.java b/org.jacoco.core/src/org/jacoco/core/data/IMethodStructureOutput.java
new file mode 100644
index 0000000..ec2238f
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/IMethodStructureOutput.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+/**
+ * Interface for data output of the internal structure of a single method. This
+ * interface is meant to be implemented by parties that want to retrieve data
+ * from the instrumentation process.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public interface IMethodStructureOutput {
+
+ /**
+ * Called for every block within the method.
+ *
+ * @param id
+ * identifier of the block within the method
+ * @param instructionCount
+ * number of byte code instructions within this block
+ * @param lineNumbers
+ * list of source lines corresponding to this block
+ */
+ public void block(int id, int instructionCount, int[] lineNumbers);
+
+ /**
+ * Called after all information for this method has been emitted.
+ */
+ public void end();
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/IStructureOutput.java b/org.jacoco.core/src/org/jacoco/core/data/IStructureOutput.java
new file mode 100644
index 0000000..afa5353
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/IStructureOutput.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+/**
+ * Interface for data output of the internal class structure. This interface is
+ * meant to be implemented by parties that want to retrieve data from the class
+ * analyzing process.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public interface IStructureOutput {
+
+ /**
+ * Provides structural information about a class as collected during
+ * instrumentation.
+ *
+ * @param id
+ * unique id for the class
+ * @param name
+ * VM name of the class
+ * @param bundle
+ * optional bundle identifier this class belongs to
+ * @return call-back for structure details about the class
+ *
+ */
+ public IClassStructureOutput classStructure(long id, String name,
+ String bundle);
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/LinesImpl.java b/org.jacoco.core/src/org/jacoco/core/data/LinesImpl.java
new file mode 100644
index 0000000..97553b3
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/LinesImpl.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+/**
+ * {@link ILines} implementation.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class LinesImpl extends AbstractCounter implements ILines {
+
+ /** status for each line */
+ private byte[] status;
+
+ /** first line number in lines */
+ private int offset;
+
+ /**
+ * Creates an empty line counter.
+ */
+ public LinesImpl() {
+ super(0, 0);
+ status = null;
+ offset = -1;
+ }
+
+ /**
+ * Created a line counter with the given lines
+ *
+ * @param lines
+ * line numbers to add
+ * @param covered
+ * <code>true</code> if all lines are covered
+ *
+ */
+ public LinesImpl(final int[] lines, final boolean covered) {
+ this();
+ increment(lines, covered);
+ }
+
+ private void ensureCapacity(final int first, final int last) {
+ if (status == null) {
+ offset = first;
+ status = new byte[last - first + 1];
+ } else {
+ final int newFirst = Math.min(getFirstLine(), first);
+ final int newLast = Math.max(getLastLine(), last);
+ final int newLength = newLast - newFirst + 1;
+ if (newLength > status.length) {
+ final byte[] newStatus = new byte[newLength];
+ System.arraycopy(status, 0, newStatus, offset - newFirst,
+ status.length);
+ offset = newFirst;
+ status = newStatus;
+ }
+ }
+ }
+
+ /**
+ * Adds the given lines as fully covered or not covered.
+ *
+ * @param lines
+ * line numbers to add
+ * @param covered
+ * <code>true</code> if all lines are covered
+ *
+ */
+ public void increment(final int[] lines, final boolean covered) {
+ if (lines.length > 0) {
+ ensureCapacity(lines[0], lines[lines.length - 1]);
+ final byte newStatus = covered ? FULLY_COVERED : NOT_COVERED;
+ for (final int line : lines) {
+ incrementLine(line, newStatus);
+ }
+ }
+ }
+
+ /**
+ * Adds the lines of the given line counter. The states are updated
+ * accordingly.
+ *
+ * @param counter
+ * line counter to add
+ */
+ public void increment(final ILines counter) {
+ if (counter.getTotalCount() == 0) {
+ return;
+ }
+ ensureCapacity(counter.getFirstLine(), counter.getLastLine());
+ for (int line = counter.getFirstLine(); line <= counter.getLastLine(); line++) {
+ incrementLine(line, counter.getStatus(line));
+ }
+ }
+
+ private void incrementLine(final int line, final byte newStatus) {
+ final byte oldStatus = status[line - offset];
+ status[line - offset] = (byte) (oldStatus | newStatus);
+ if (oldStatus == NO_CODE && newStatus != NO_CODE) {
+ total++;
+ }
+ if ((oldStatus == NO_CODE || oldStatus == NOT_COVERED)
+ && (newStatus == PARTLY_COVERED || newStatus == FULLY_COVERED)) {
+ covered++;
+ }
+ }
+
+ // === ILineCounter ===
+
+ public int getFirstLine() {
+ return offset;
+ }
+
+ public int getLastLine() {
+ return status == null ? -1 : (offset + status.length - 1);
+ }
+
+ public byte getStatus(final int line) {
+ if (status == null || line < getFirstLine() || line > getLastLine()) {
+ return NO_CODE;
+ }
+ return status[line - offset];
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/MethodCoverageData.java b/org.jacoco.core/src/org/jacoco/core/data/MethodCoverageData.java
new file mode 100644
index 0000000..49ab629
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/MethodCoverageData.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+import java.util.Collection;
+
+/**
+ * Coverage data of a single method.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class MethodCoverageData extends CoverageDataImpl {
+
+ private final String name;
+ private final String desc;
+ private final String signature;
+
+ /**
+ * Creates a method coverage data object with the given parameters.
+ *
+ * @param name
+ * name of the method
+ * @param desc
+ * parameter description
+ * @param signature
+ * generic signature or <code>null</code>
+ * @param blocks
+ * contained blocks
+ */
+ public MethodCoverageData(final String name, final String desc,
+ final String signature, final Collection<ICoverageData> blocks) {
+ super(ElementType.METHOD, true);
+ this.name = name;
+ this.desc = desc;
+ this.signature = signature;
+ addAll(blocks);
+ // A method is considered as covered when at least one block is covered:
+ final boolean covered = getBlockCounter().getCoveredCount() > 0;
+ methodCounter = CounterImpl.getInstance(covered);
+ }
+
+ /**
+ * Return the name of the method.
+ *
+ * @return name of the method
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the parameter description of the method.
+ *
+ * @return parameter description
+ */
+ public String getDesc() {
+ return desc;
+ }
+
+ /**
+ * Returns the generic signature of the method if defined.
+ *
+ * @return generic signature or <code>null</code>
+ */
+ public String getSignature() {
+ return signature;
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/SourceFileCoverageData.java b/org.jacoco.core/src/org/jacoco/core/data/SourceFileCoverageData.java
new file mode 100644
index 0000000..4c1ab77
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/SourceFileCoverageData.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.data;
+
+/**
+ * Coverage data of a single source file. As source file contains one or more
+ * classes.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class SourceFileCoverageData extends CoverageDataImpl {
+
+ private final String bundle;
+
+ private final String packagename;
+
+ private final String filename;
+
+ /**
+ * Creates a source file data object with the given parameters.
+ *
+ * @param filename
+ * name of the source file
+ * @param packagename
+ * vm name of the package the source file belongs to
+ * @param bundle
+ * optional bundle name
+ */
+ public SourceFileCoverageData(final String filename,
+ final String packagename, final String bundle) {
+ super(ElementType.SOURCEFILE, true);
+ this.bundle = bundle;
+ this.packagename = packagename;
+ this.filename = filename;
+ }
+
+ /**
+ * Returns the source file name.
+ *
+ * @return source file name
+ */
+ public String getFilename() {
+ return filename;
+ }
+
+ /**
+ * Returns the vm name of the package the source file belongs to.
+ *
+ * @return package name
+ */
+ public String getPackagename() {
+ return packagename;
+ }
+
+ /**
+ * Returns an optional bundle identifier.
+ *
+ * @return bundle identifier or <code>null</code>
+ */
+ public String getBundle() {
+ return bundle;
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/package.html b/org.jacoco.core/src/org/jacoco/core/data/package.html
new file mode 100644
index 0000000..9212000
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/package.html
@@ -0,0 +1,9 @@
+<html>
+<body>
+
+<p>
+ This package contains the classes to calculate and store coverage data.
+</p>
+
+</body>
+</html>
diff --git a/org.jacoco.core/src/org/jacoco/core/instr/Analyzer.java b/org.jacoco.core/src/org/jacoco/core/instr/Analyzer.java
new file mode 100644
index 0000000..ded601c
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/instr/Analyzer.java
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.instr;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.StringTokenizer;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.jacoco.core.data.IClassStructureOutput;
+import org.jacoco.core.data.IStructureOutput;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Several APIs to analyze class structures.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class Analyzer {
+
+ private final IStructureOutput structureOutput;
+
+ /**
+ * Creates a new analyzer reporting to the given output.
+ *
+ * @param structureOutput
+ * the output instance that will receive all structure data
+ */
+ public Analyzer(IStructureOutput structureOutput) {
+ this.structureOutput = structureOutput;
+ }
+
+ /**
+ * Creates an ASM class visitor for analysis.
+ *
+ * @param classid
+ * id of the class calculated with {@link CRC64}
+ * @param classname
+ * VM name of the class
+ * @param bundle
+ * optional bundle this class belongs to
+ * @return ASM visitor to write class definition to
+ */
+ public ClassVisitor createAnalyzingVisitor(long classid, String classname,
+ String bundle) {
+ final IClassStructureOutput classStructure = structureOutput
+ .classStructure(classid, classname, bundle);
+ return new ClassAnalyzer(classStructure);
+ }
+
+ /**
+ * Analyzes the class given as a ASM reader.
+ *
+ * @param reader
+ * reader with class definitions
+ * @param bundle
+ * optional bundle this class belongs to
+ */
+ public void analyze(ClassReader reader, String bundle) {
+ if ((reader.getAccess() & Opcodes.ACC_INTERFACE) != 0) {
+ return;
+ }
+
+ final ClassVisitor visitor = createAnalyzingVisitor(CRC64
+ .checksum(reader.b), reader.getClassName(), bundle);
+ reader.accept(visitor, 0);
+ }
+
+ /**
+ * Analyzes the class definition from a given in-memory buffer.
+ *
+ * @param buffer
+ * class definitions
+ * @param bundle
+ * optional bundle this class belongs to
+ */
+ public void analyze(byte[] buffer, String bundle) {
+ analyze(new ClassReader(buffer), bundle);
+ }
+
+ /**
+ * Analyzes the class definition from a given input stream.
+ *
+ * @param input
+ * stream to read class definition from
+ * @param bundle
+ * optional bundle this class belongs to
+ * @throws IOException
+ */
+ public void analyze(InputStream input, String bundle) throws IOException {
+ analyze(new ClassReader(input), bundle);
+ }
+
+ /**
+ * Analyzes the class definition contained in a given file.
+ *
+ * @param file
+ * class file
+ * @param bundle
+ * optional bundle this class belongs to
+ * @throws IOException
+ */
+ public void analyze(File file, String bundle) throws IOException {
+ final InputStream in = new FileInputStream(file);
+ analyze(new ClassReader(in), bundle);
+ in.close();
+ }
+
+ /**
+ * Analyzes all class files contained in the given directory and its
+ * children.
+ *
+ * @param directory
+ * folder to look for class files
+ * @param bundle
+ * optional bundle all the classes belong to
+ * @throws IOException
+ */
+ public void analyzeAll(File directory, String bundle) throws IOException {
+ for (final File f : directory.listFiles()) {
+ if (f.isDirectory()) {
+ analyzeAll(directory, bundle);
+ continue;
+ }
+ if (f.getName().endsWith(".class")) {
+ analyze(f, bundle);
+ }
+ }
+ }
+
+ /**
+ * Analyzes all class files contained in a JAR file.
+ *
+ * @param input
+ * stream to read the JAR file from
+ * @param bundle
+ * optional bundle all the classes belong to
+ * @throws IOException
+ */
+ public void analyzeJAR(InputStream input, String bundle) throws IOException {
+ final ZipInputStream zip = new ZipInputStream(input);
+ while (true) {
+ final ZipEntry entry = zip.getNextEntry();
+ if (entry == null) {
+ break;
+ }
+ if (entry.getName().endsWith(".class")) {
+ analyze(zip, bundle);
+ }
+ }
+ }
+
+ /**
+ * Analyzes all class files contained in a JAR file.
+ *
+ * @param jarfile
+ * JAR file
+ * @param bundle
+ * optional bundle all the classes belong to
+ * @throws IOException
+ */
+ public void analyzeJAR(File jarfile, String bundle) throws IOException {
+ final InputStream in = new FileInputStream(jarfile);
+ analyzeJAR(in, bundle);
+ in.close();
+ }
+
+ /**
+ * Analyzes all class from the given class path.
+ *
+ * @param path
+ * path definition
+ * @param basedir
+ * optional base directory, if <code>null</code> the current
+ * working directory is used as the base for relative path
+ * entries
+ * @param bundle
+ * optional bundle all the classes belong to
+ * @throws IOException
+ */
+ public void analyzePath(String path, File basedir, String bundle)
+ throws IOException {
+ final StringTokenizer tokenizer = new StringTokenizer(path,
+ File.pathSeparator);
+ while (tokenizer.hasMoreTokens()) {
+ final File entry = new File(basedir, tokenizer.nextToken());
+ if (entry.isDirectory()) {
+ analyzeAll(entry, bundle);
+ continue;
+ }
+ if (entry.isFile() && entry.getName().endsWith(".jar")) {
+ analyzeJAR(entry, bundle);
+ }
+ }
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/instr/BlockMethodAdapter.java b/org.jacoco.core/src/org/jacoco/core/instr/BlockMethodAdapter.java
new file mode 100644
index 0000000..1231e6f
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/instr/BlockMethodAdapter.java
@@ -0,0 +1,247 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.instr;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * A method visitor that determines block boundaries and reports them to the
+ * wrapped {@link IBlockMethodVisitor}. The implementation first buffers the
+ * content of the method to extract all control flow target labels. At the end
+ * of the method it flushes the content to the {@link IBlockMethodVisitor}.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public final class BlockMethodAdapter extends MethodNode {
+
+ private final IBlockMethodVisitor blockVisitor;
+
+ private final Set<Label> targetLabels;
+
+ private int blockCount;
+
+ /**
+ * Create a new adapter for the given block visitor.
+ *
+ * @param blockVisitor
+ * visitor to report block boundaries to
+ * @param access
+ * the method's access flags
+ * @param name
+ * the method's name.
+ * @param desc
+ * the method's descriptor
+ * @param signature
+ * the method's signature. May be <tt>null</tt>.
+ * @param exceptions
+ * the internal names of the method's exception classes. May be
+ * <tt>null</tt>.
+ */
+ public BlockMethodAdapter(IBlockMethodVisitor blockVisitor,
+ final int access, final String name, final String desc,
+ final String signature, final String[] exceptions) {
+ super(access, name, desc, signature, exceptions);
+ this.blockVisitor = blockVisitor;
+ this.targetLabels = new HashSet<Label>();
+ this.blockCount = 0;
+ }
+
+ /**
+ * Returns the number of blocks found in the method. A valid return value
+ * can only be expected after {@link #visitEnd()} has been called.
+ *
+ * @return number of block in the method
+ */
+ public int getBlockCount() {
+ return blockCount;
+ }
+
+ // === MethodVisitor ===
+
+ @Override
+ public void visitJumpInsn(int opcode, Label label) {
+ targetLabels.add(label);
+ super.visitJumpInsn(opcode, label);
+ }
+
+ @Override
+ public void visitTableSwitchInsn(int min, int max, Label dflt,
+ Label[] labels) {
+ targetLabels.add(dflt);
+ for (final Label l : labels) {
+ targetLabels.add(l);
+ }
+ super.visitTableSwitchInsn(min, max, dflt, labels);
+ }
+
+ @Override
+ public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+ targetLabels.add(dflt);
+ for (final Label l : labels) {
+ targetLabels.add(l);
+ }
+ super.visitLookupSwitchInsn(dflt, keys, labels);
+ }
+
+ @Override
+ public void visitTryCatchBlock(Label start, Label end, Label handler,
+ String type) {
+ targetLabels.add(start);
+ targetLabels.add(end);
+ targetLabels.add(handler);
+ super.visitTryCatchBlock(start, end, handler, type);
+ }
+
+ @Override
+ public void visitEnd() {
+ accept(new BlockFinder());
+ }
+
+ private final class BlockFinder extends MethodAdapter {
+
+ private boolean blockStarted;
+
+ public BlockFinder() {
+ super(blockVisitor);
+ blockStarted = false;
+ }
+
+ private void onBlockEndBeforeJump() {
+ if (blockStarted) {
+ blockVisitor.visitBlockEndBeforeJump(blockCount);
+ }
+ }
+
+ private void onBlockEnd() {
+ if (blockStarted) {
+ blockVisitor.visitBlockEnd(blockCount);
+ blockCount++;
+ blockStarted = false;
+ }
+ }
+
+ @Override
+ public void visitLabel(Label label) {
+ if (targetLabels.contains(label)) {
+ onBlockEndBeforeJump();
+ onBlockEnd();
+ }
+ super.visitLabel(label);
+ }
+
+ @Override
+ public void visitJumpInsn(int opcode, Label label) {
+ blockStarted = true;
+ onBlockEndBeforeJump();
+ super.visitJumpInsn(opcode, label);
+ onBlockEnd();
+ }
+
+ @Override
+ public void visitInsn(int opcode) {
+ blockStarted = true;
+ switch (opcode) {
+ case Opcodes.RETURN:
+ case Opcodes.IRETURN:
+ case Opcodes.FRETURN:
+ case Opcodes.LRETURN:
+ case Opcodes.DRETURN:
+ case Opcodes.ARETURN:
+ case Opcodes.ATHROW:
+ onBlockEndBeforeJump();
+ super.visitInsn(opcode);
+ onBlockEnd();
+ break;
+ default:
+ super.visitInsn(opcode);
+ break;
+ }
+ }
+
+ @Override
+ public void visitTableSwitchInsn(int min, int max, Label dflt,
+ Label[] labels) {
+ blockStarted = true;
+ onBlockEndBeforeJump();
+ super.visitTableSwitchInsn(min, max, dflt, labels);
+ onBlockEnd();
+ }
+
+ @Override
+ public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+ blockStarted = true;
+ onBlockEndBeforeJump();
+ super.visitLookupSwitchInsn(dflt, keys, labels);
+ onBlockEnd();
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name,
+ String desc) {
+ blockStarted = true;
+ super.visitFieldInsn(opcode, owner, name, desc);
+ }
+
+ @Override
+ public void visitIincInsn(int var, int increment) {
+ blockStarted = true;
+ super.visitIincInsn(var, increment);
+ }
+
+ @Override
+ public void visitIntInsn(int opcode, int operand) {
+ blockStarted = true;
+ super.visitIntInsn(opcode, operand);
+ }
+
+ @Override
+ public void visitLdcInsn(Object cst) {
+ blockStarted = true;
+ super.visitLdcInsn(cst);
+ }
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name,
+ String desc) {
+ blockStarted = true;
+ super.visitMethodInsn(opcode, owner, name, desc);
+ }
+
+ @Override
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+ blockStarted = true;
+ super.visitMultiANewArrayInsn(desc, dims);
+ }
+
+ @Override
+ public void visitTypeInsn(int opcode, String type) {
+ blockStarted = true;
+ super.visitTypeInsn(opcode, type);
+ }
+
+ @Override
+ public void visitVarInsn(int opcode, int var) {
+ blockStarted = true;
+ super.visitVarInsn(opcode, var);
+ }
+
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/instr/CRC64.java b/org.jacoco.core/src/org/jacoco/core/instr/CRC64.java
new file mode 100644
index 0000000..b96fd51
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/instr/CRC64.java
@@ -0,0 +1,55 @@
+package org.jacoco.core.instr;
+
+/**
+ * CRC64 checksum calculator based on the polynom specified in ISO 3309. The
+ * implementation is based on the following publications:
+ *
+ * <ul>
+ * <li>http://en.wikipedia.org/wiki/Cyclic_redundancy_check</li>
+ * <li>http://www.geocities.com/SiliconValley/Pines/8659/crc.htm</li>
+ * </ul>
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public final class CRC64 {
+
+ private static final long POLY64REV = 0xd800000000000000L;
+
+ private static final long[] LOOKUPTABLE;
+
+ static {
+ LOOKUPTABLE = new long[0x100];
+ for (int i = 0; i < 0x100; i++) {
+ long v = i;
+ for (int j = 0; j < 8; j++) {
+ if ((v & 1) == 1) {
+ v = (v >>> 1) ^ POLY64REV;
+ } else {
+ v = (v >>> 1);
+ }
+ }
+ LOOKUPTABLE[i] = v;
+ }
+ }
+
+ /**
+ * Calculates the CRC64 checksum for the given data array.
+ *
+ * @param data
+ * data to calculate checksum for
+ * @return checksum value
+ */
+ public static long checksum(byte[] data) {
+ long sum = 0;
+ for (int i = 0; i < data.length; i++) {
+ final int lookupidx = ((int) sum ^ data[i]) & 0xff;
+ sum = (sum >>> 8) ^ LOOKUPTABLE[lookupidx];
+ }
+ return sum;
+ }
+
+ private CRC64() {
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/instr/ClassAnalyzer.java b/org.jacoco.core/src/org/jacoco/core/instr/ClassAnalyzer.java
new file mode 100644
index 0000000..b9a3ddd
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/instr/ClassAnalyzer.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.instr;
+
+import org.jacoco.core.data.IClassStructureOutput;
+import org.jacoco.core.data.IMethodStructureOutput;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+/**
+ * A {@link ClassVisitor} that analyzes the executable blocks of a class.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class ClassAnalyzer extends EmptyVisitor {
+
+ private final IClassStructureOutput structureOutput;
+
+ private int methodCount;
+
+ /**
+ * Creates a new analyzer that reports to the given
+ * {@link IClassStructureOutput} instance.
+ *
+ * @param structureOutput
+ * consumer for class structure output
+ */
+ public ClassAnalyzer(IClassStructureOutput structureOutput) {
+ this.structureOutput = structureOutput;
+ methodCount = 0;
+ }
+
+ @Override
+ public void visitSource(String source, String debug) {
+ if (source != null) {
+ structureOutput.sourceFile(source);
+ }
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc,
+ String signature, String[] exceptions) {
+
+ // Abstract methods do not have code to analyze
+ if ((access & Opcodes.ACC_ABSTRACT) != 0) {
+ return null;
+ }
+
+ final IMethodStructureOutput structure = structureOutput
+ .methodStructure(methodCount++, name, desc, signature);
+ return new BlockMethodAdapter(new MethodAnalyzer(structure), access,
+ name, desc, signature, exceptions);
+ }
+
+ @Override
+ public void visitEnd() {
+ structureOutput.end();
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/instr/ClassInstrumenter.java b/org.jacoco.core/src/org/jacoco/core/instr/ClassInstrumenter.java
new file mode 100644
index 0000000..afe2fb1
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/instr/ClassInstrumenter.java
@@ -0,0 +1,204 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.instr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jacoco.core.runtime.IRuntime;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.EmptyVisitor;
+import org.objectweb.asm.commons.GeneratorAdapter;
+
+/**
+ * Adapter that instruments a class for coverage tracing.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class ClassInstrumenter extends ClassAdapter {
+
+ private static class EmptyBlockMethodVisitor extends EmptyVisitor implements
+ IBlockMethodVisitor {
+
+ public void visitBlockEndBeforeJump(final int id) {
+ }
+
+ public void visitBlockEnd(final int id) {
+ }
+
+ }
+
+ private final long id;
+
+ private final IRuntime runtime;
+
+ private final List<BlockMethodAdapter> blockCounters;
+
+ private Type type;
+
+ /**
+ * Emits a instrumented version of this class to the given class visitor
+ *
+ * @param id
+ * unique identifier given to this class
+ * @param runtime
+ * this runtime will be used for instrumentation
+ * @param cv
+ * next delegate in the visitor chain will receive the
+ * instrumented class
+ */
+ public ClassInstrumenter(final long id, final IRuntime runtime,
+ final ClassVisitor cv) {
+ super(cv);
+ this.id = id;
+ this.runtime = runtime;
+ this.blockCounters = new ArrayList<BlockMethodAdapter>();
+ }
+
+ @Override
+ public void visit(final int version, final int access, final String name,
+ final String signature, final String superName,
+ final String[] interfaces) {
+ this.type = Type.getObjectType(name);
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(final int access, final String name,
+ final String desc, final String signature, final String[] exceptions) {
+
+ final MethodVisitor mv = super.visitMethod(access, name, desc,
+ signature, exceptions);
+
+ // Abstract methods do not have code to analyze
+ if ((access & Opcodes.ACC_ABSTRACT) != 0) {
+ return mv;
+ }
+
+ final int methodId = blockCounters.size();
+
+ final IBlockMethodVisitor blockVisitor;
+ if (mv == null) {
+ blockVisitor = new EmptyBlockMethodVisitor();
+ } else {
+ blockVisitor = new MethodInstrumenter(mv, access, name, desc,
+ methodId, type);
+ }
+
+ final BlockMethodAdapter adapter = new BlockMethodAdapter(blockVisitor,
+ access, name, desc, signature, exceptions);
+ blockCounters.add(adapter);
+ return adapter;
+ }
+
+ @Override
+ public void visitEnd() {
+ createDataField();
+ createInitMethod();
+ super.visitEnd();
+ }
+
+ private void createDataField() {
+ super.visitField(GeneratorConstants.DATAFIELD_ACC,
+ GeneratorConstants.DATAFIELD_NAME,
+ GeneratorConstants.DATAFIELD_TYPE.getDescriptor(), null, null);
+ }
+
+ private void createInitMethod() {
+ final int access = GeneratorConstants.INIT_METHOD_ACC;
+ final String name = GeneratorConstants.INIT_METHOD.getName();
+ final String desc = GeneratorConstants.INIT_METHOD.getDescriptor();
+ final GeneratorAdapter gen = new GeneratorAdapter(super.visitMethod(
+ access, name, desc, null, null), access, name, desc);
+
+ // Load the value of the static data field:
+ gen.getStatic(type, GeneratorConstants.DATAFIELD_NAME,
+ GeneratorConstants.DATAFIELD_TYPE);
+ gen.dup();
+
+ // .............................................. Stack: [[Z, [[Z
+
+ // Skip initialization when we already have a data array:
+ final Label alreadyInitialized = new Label();
+ gen.ifNonNull(alreadyInitialized);
+
+ // .............................................. Stack: [[Z
+
+ gen.pop();
+ genInitializeDataField(gen);
+
+ // .............................................. Stack: [[Z
+
+ // Return the method's block array:
+ gen.visitLabel(alreadyInitialized);
+ gen.loadArg(0);
+ gen.arrayLoad(GeneratorConstants.BLOCK_ARR);
+ gen.returnValue();
+ gen.endMethod();
+ }
+
+ /**
+ * Generates the byte code to initialize the static coverage data field
+ * within this class.
+ *
+ * The code will push the [[Z data array on the operand stack.
+ *
+ * @param gen
+ * generator to emit code to
+ */
+ private void genInitializeDataField(final GeneratorAdapter gen) {
+ genInstantiateDataArray(gen); // ................ Stack: [[Z
+ gen.dup(); // ................................... Stack: [[Z [[Z
+ gen.putStatic(type, GeneratorConstants.DATAFIELD_NAME,
+ GeneratorConstants.DATAFIELD_TYPE);
+
+ // .............................................. Stack: [[Z
+
+ gen.dup(); // ................................... Stack: [[Z [[Z
+ runtime.generateRegistration(id, gen);
+
+ // .............................................. Stack: [[Z
+ }
+
+ /**
+ * Generates the byte code to instantiate the 2-dimensional block data
+ * array. Each boolean[] entry is created in a length equals to the number
+ * of blocks in the corresponding method.
+ *
+ * The code will push the [[Z data array on the operand stack.
+ *
+ * TODO: Let the coverage runtime generate this structure
+ *
+ * @param gen
+ * generator to emit code to
+ */
+ private void genInstantiateDataArray(final GeneratorAdapter gen) {
+ gen.push(blockCounters.size()); // .............. Stack: I
+ gen.newArray(GeneratorConstants.BLOCK_ARR); // .. Stack: [[Z
+ for (int blockIdx = 0; blockIdx < blockCounters.size(); blockIdx++) {
+ gen.dup(); // ............................... Stack: [[Z, [[Z
+ gen.push(blockIdx); // ...................... Stack: [[Z, [[Z, I
+ gen.push(blockCounters.get(blockIdx).getBlockCount());
+ // .......................................... Stack: [[Z, [[Z, I, I
+ gen.newArray(Type.BOOLEAN_TYPE);// .......... Stack: [[Z, [[Z, I, [Z
+ gen.arrayStore(GeneratorConstants.BLOCK_ARR); // Stack: [[Z
+ }
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/instr/GeneratorConstants.java b/org.jacoco.core/src/org/jacoco/core/instr/GeneratorConstants.java
new file mode 100644
index 0000000..cb7ca29
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/instr/GeneratorConstants.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.instr;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.Method;
+
+/**
+ * Constants for generated instrumentation code.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public final class GeneratorConstants {
+
+ /**
+ * Type for array of primitive boolean values. This type is used to store
+ * covered blocks of a single method.
+ */
+ public static final Type BLOCK_ARR = Type.getType("[Z");
+
+ // === Data Field ===
+
+ /**
+ * Name of the field that stores coverage information of a class.
+ */
+ public static final String DATAFIELD_NAME = "$jacocoData";
+
+ /**
+ * Access modifiers of the field that stores coverage information of a
+ * class.
+ */
+ public static final int DATAFIELD_ACC = Opcodes.ACC_SYNTHETIC
+ | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
+
+ /**
+ * The type of the field that stores coverage information of a class is a
+ * 2-dimensional array of primitive boolean values.
+ */
+ public static final Type DATAFIELD_TYPE = Type.getType("[[Z");
+
+ // === Init Method ===
+
+ /**
+ * Initialization method that is added into every instrumented class.
+ */
+ public static final Method INIT_METHOD = new Method("$jacocoInit", "(I)[Z");
+
+ /**
+ * Access modifiers of the initialization method.
+ */
+ public static final int INIT_METHOD_ACC = Opcodes.ACC_SYNTHETIC
+ | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/instr/IBlockMethodVisitor.java b/org.jacoco.core/src/org/jacoco/core/instr/IBlockMethodVisitor.java
new file mode 100644
index 0000000..243bed5
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/instr/IBlockMethodVisitor.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.instr;
+
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A method visitor with additional notifications for block boundaries.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public interface IBlockMethodVisitor extends MethodVisitor {
+
+ /**
+ * This method is called at the end of a block. If the last instruction of
+ * the block may interrupt the control flow (e.g. jump or return) this
+ * method is called right before this statement is visited.
+ *
+ * @param id
+ * identifier of the block within the method
+ */
+ public void visitBlockEndBeforeJump(int id);
+
+ /**
+ * This method is always called after the last instruction of a block.
+ *
+ * @param id
+ * identifier of the block within the method
+ */
+ public void visitBlockEnd(int id);
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/instr/ITargetLabelSource.java b/org.jacoco.core/src/org/jacoco/core/instr/ITargetLabelSource.java
new file mode 100644
index 0000000..a85b4be
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/instr/ITargetLabelSource.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.instr;
+
+import org.objectweb.asm.Label;
+
+/**
+ * Call-back interface to find labels used as control flow targets.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public interface ITargetLabelSource {
+
+ /**
+ * Checks whether the given label is control flow target (jump, switch,
+ * try/catch etc).
+ *
+ * @param label
+ * label to check
+ * @return <code>true</code>, if the label is a control flow target
+ */
+ public boolean isTargetLabel(Label label);
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java b/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java
new file mode 100644
index 0000000..cb2ca20
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.instr;
+
+import org.jacoco.core.runtime.IRuntime;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Several APIs to instrument Java class definitions for coverage tracing.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class Instrumenter {
+
+ private final IRuntime runtime;
+
+ /**
+ * Creates a new instance based on the given runtime.
+ *
+ * @param runtime
+ * runtime used by the instrumented classes
+ */
+ public Instrumenter(final IRuntime runtime) {
+ this.runtime = runtime;
+ }
+
+ /**
+ * Creates a ASM adapter for a class with the given id.
+ *
+ * @param classid
+ * id of the class calculated with {@link CRC64}
+ * @param cv
+ * next class visitor in the chain
+ * @return new visitor to write class definition to
+ */
+ public ClassVisitor createInstrumentingVisitor(final long classid,
+ final ClassVisitor cv) {
+ return new ClassInstrumenter(classid, runtime, cv);
+ }
+
+ /**
+ * Creates a instrumented version of the given class if possible.
+ *
+ * @param reader
+ * definition of the class as ASM reader
+ * @return instrumented definition or <code>null</code>
+ *
+ */
+ public byte[] instrument(final ClassReader reader) {
+
+ // Don't instrument interfaces
+ if ((reader.getAccess() & Opcodes.ACC_INTERFACE) != 0) {
+ return null;
+ }
+
+ final ClassWriter writer = new ClassWriter(reader,
+ ClassWriter.COMPUTE_MAXS);
+ final ClassVisitor visitor = createInstrumentingVisitor(CRC64
+ .checksum(reader.b), writer);
+ reader.accept(visitor, 0);
+ return writer.toByteArray();
+ }
+
+ /**
+ * Creates a instrumented version of the given class if possible.
+ *
+ * @param buffer
+ * definition of the class
+ * @return instrumented definition or <code>null</code>
+ *
+ */
+ public byte[] instrument(final byte[] buffer) {
+ return instrument(new ClassReader(buffer));
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/instr/IntSet.java b/org.jacoco.core/src/org/jacoco/core/instr/IntSet.java
new file mode 100644
index 0000000..17cc318
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/instr/IntSet.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.instr;
+
+import java.util.Arrays;
+
+/**
+ * Lightweight set of sorted int values. The implementation is designed for a
+ * small number of elements only.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+final class IntSet {
+
+ private int[] store;
+
+ private int size;
+
+ /**
+ * Creates a new empty set with a initial capacity of 8 elements.
+ */
+ public IntSet() {
+ this(8);
+ }
+
+ /**
+ * Creates a new empty set with the given initial capacity.
+ *
+ * @param initialCapacity
+ * this is the initial capacity
+ */
+ public IntSet(int initialCapacity) {
+ this.store = new int[initialCapacity];
+ this.size = 0;
+ }
+
+ /**
+ * Adds the given value to the set if it is not already contained.
+ *
+ * @param value
+ * value to add
+ * @return <code>true</code> if the value has actually been added
+ */
+ public boolean add(int value) {
+ if (contains(value)) {
+ return false;
+ }
+ if (store.length == size) {
+ final int[] newStore = new int[size * 2];
+ System.arraycopy(store, 0, newStore, 0, size);
+ store = newStore;
+ }
+ store[size++] = value;
+ return true;
+ }
+
+ /**
+ * Clears all elements from the set.
+ */
+ public void clear() {
+ size = 0;
+ }
+
+ /**
+ * Tests whether the given value is in this set.
+ *
+ * @param value
+ * value to check
+ * @return <code>true</code> if the value is contained
+ */
+ public boolean contains(int value) {
+ // search backwards as the last value is typically added again
+ for (int i = size; --i >= 0;) {
+ if (store[i] == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns a sorted array of the current content.
+ *
+ * @return sorted array of the current content
+ */
+ public int[] toArray() {
+ final int[] result = new int[size];
+ System.arraycopy(store, 0, result, 0, size);
+ Arrays.sort(result);
+ return result;
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/instr/MethodAnalyzer.java b/org.jacoco.core/src/org/jacoco/core/instr/MethodAnalyzer.java
new file mode 100644
index 0000000..cd35dff
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/instr/MethodAnalyzer.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.instr;
+
+import org.jacoco.core.data.IMethodStructureOutput;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+/**
+ * This {@link IBlockMethodVisitor} analyzes the block structure of a method and
+ * reports it to a {@link IMethodStructureOutput} instance.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public final class MethodAnalyzer extends EmptyVisitor implements
+ IBlockMethodVisitor {
+
+ private static final int NO_LINE_INFO = -1;
+
+ private final IMethodStructureOutput structureOutput;
+
+ private int instructionCount;
+
+ private int currentLine;
+
+ private final IntSet lineNumbers;
+
+ /**
+ * Creates a new analyzer that reports to the given
+ * {@link IMethodStructureOutput} instance.
+ *
+ * @param structureOutput
+ * consumer for method structure events
+ */
+ public MethodAnalyzer(IMethodStructureOutput structureOutput) {
+ this.structureOutput = structureOutput;
+ this.instructionCount = 0;
+ this.currentLine = NO_LINE_INFO;
+ this.lineNumbers = new IntSet();
+ }
+
+ private void addInstruction() {
+ instructionCount++;
+ if (currentLine != NO_LINE_INFO) {
+ lineNumbers.add(currentLine);
+ }
+ }
+
+ // === MethodVisitor ===
+
+ @Override
+ public void visitLineNumber(int line, Label start) {
+ currentLine = line;
+ }
+
+ @Override
+ public void visitJumpInsn(int opcode, Label label) {
+ addInstruction();
+ }
+
+ @Override
+ public void visitInsn(int opcode) {
+ addInstruction();
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name,
+ String desc) {
+ addInstruction();
+ }
+
+ @Override
+ public void visitIincInsn(int var, int increment) {
+ addInstruction();
+ }
+
+ @Override
+ public void visitIntInsn(int opcode, int operand) {
+ addInstruction();
+ }
+
+ @Override
+ public void visitLdcInsn(Object cst) {
+ addInstruction();
+ }
+
+ @Override
+ public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+ addInstruction();
+ }
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name,
+ String desc) {
+ addInstruction();
+ }
+
+ @Override
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+ addInstruction();
+ }
+
+ @Override
+ public void visitTableSwitchInsn(int min, int max, Label dflt,
+ Label[] labels) {
+ addInstruction();
+ }
+
+ @Override
+ public void visitTypeInsn(int opcode, String type) {
+ addInstruction();
+ }
+
+ @Override
+ public void visitVarInsn(int opcode, int var) {
+ addInstruction();
+ }
+
+ @Override
+ public void visitEnd() {
+ structureOutput.end();
+ }
+
+ // === IBlockVisitor ===
+
+ public void visitBlockEndBeforeJump(int id) {
+ }
+
+ public void visitBlockEnd(int id) {
+ structureOutput.block(id, instructionCount, lineNumbers.toArray());
+ instructionCount = 0;
+ lineNumbers.clear();
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/instr/MethodInstrumenter.java b/org.jacoco.core/src/org/jacoco/core/instr/MethodInstrumenter.java
new file mode 100644
index 0000000..fc7c6ee
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/instr/MethodInstrumenter.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.instr;
+
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.GeneratorAdapter;
+
+/**
+ * This method adapter instruments a method to record every block that gets
+ * fully executed.
+ *
+ * TODO The instrumented method may need a increased stack but this adapter will
+ * *not* adjust the max stack size value. Therefore the ClassWriter needs to be
+ * invoked with COMPUTE_MAXS. For performance optimization this adapter should
+ * adjust the directly calculate the max stack size and report it properly.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class MethodInstrumenter extends GeneratorAdapter implements
+ IBlockMethodVisitor {
+
+ private final int methodId;
+
+ private final Type enclosingType;
+
+ private int blockArray;
+
+ /**
+ * Create a new instrumenter instance for the given method.
+ *
+ * @param mv
+ * next method visitor in the chain
+ * @param access
+ * access flags for the method
+ * @param name
+ * name of the method
+ * @param desc
+ * description of the method
+ * @param methodId
+ * unique id of the method within its enclosing type
+ * @param enclosingType
+ * type enclosing this method
+ */
+ public MethodInstrumenter(MethodVisitor mv, int access, String name,
+ String desc, int methodId, Type enclosingType) {
+ super(mv, access, name, desc);
+ this.methodId = methodId;
+ this.enclosingType = enclosingType;
+ }
+
+ @Override
+ public void visitCode() {
+ super.visitCode();
+ // At the very beginning of the method we load the boolean[] array into
+ // a local variable that stores the block coverage of this method.
+
+ push(methodId); // ................................... Stack: I
+ invokeStatic(enclosingType, GeneratorConstants.INIT_METHOD); // ... Stack: [Z
+ blockArray = newLocal(GeneratorConstants.BLOCK_ARR);
+ storeLocal(blockArray); // ........................... Stack: <empty>
+ }
+
+ // === IBlockMethodVisitor ===
+
+ public void visitBlockEndBeforeJump(int id) {
+ // At the end of every block we set the corresponding position in the
+ // boolean[] array to true.
+
+ loadLocal(blockArray); // ............................ Stack: [Z
+ push(id); // ......................................... Stack: [Z, I
+ push(1); // .......................................... Stack: [Z, I, I
+ visitInsn(Opcodes.BASTORE); // ....................... Stack: <empty>
+ }
+
+ public void visitBlockEnd(int id) {
+ // nothing to do here
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/instr/package.html b/org.jacoco.core/src/org/jacoco/core/instr/package.html
new file mode 100644
index 0000000..7ae30a9
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/instr/package.html
@@ -0,0 +1,16 @@
+<html>
+<body>
+<p>
+ This package contains the classes to analyze and instrument classes for code
+ coverage. The main entry points are:
+</p>
+
+<ul>
+ <li><a href="Analyzer.html">Analyzer</a>: Class structure information for
+ analysis and report generation.</li>
+ <li><a href="Instrumenter.html">Instrumenter</a>: Classes instrumentation for
+ tracing coverage at runtime.</li>
+</ul>
+
+</body>
+</html>
diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/IRuntime.java b/org.jacoco.core/src/org/jacoco/core/runtime/IRuntime.java
new file mode 100644
index 0000000..273321a
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/runtime/IRuntime.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ * $Id: $
+ *******************************************************************************/
+package org.jacoco.core.runtime;
+
+import org.jacoco.core.data.IExecutionDataOutput;
+import org.objectweb.asm.commons.GeneratorAdapter;
+
+/**
+ * This interface represents a particular mechanism to collect execution
+ * information in the target VM at runtime.
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public interface IRuntime {
+
+ /**
+ * This method generates the byte code required to register the coverage
+ * data structure of the class with the given id. Typically the
+ * instrumentation process will embed this code into a method that is called
+ * on class initialization. This method can be called at any time even
+ * outside the target VM.
+ *
+ * The generated code must pop a <code>byte[][]</code> instance from the
+ * operand stack. Except this object on the stack the generated code must
+ * not make any assumptions about the structure of the embedding method or
+ * class.
+ *
+ * @param classId
+ * identifier of the class
+ * @param gen
+ * code output
+ */
+ public void generateRegistration(long classId, GeneratorAdapter gen);
+
+ /**
+ * Starts the coverage runtime. This method MUST be called before any class
+ * instrumented for this runtime is loaded.
+ */
+ public void startup();
+
+ /**
+ * Allows the coverage runtime to cleanup internals. This class should be
+ * called when classes instrumented for this runtime are not used any more.
+ */
+ public void shutdown();
+
+ /**
+ * Collects the current execution data and writes it to the given
+ * {@link IExecutionDataOutput} object. This method must only be called
+ * between {@link #startup()} and {@link #shutdown()}.
+ *
+ * @param output
+ * handler to write coverage data to
+ * @param reset
+ * if <code>true</code> the current coverage information is also
+ * cleared
+ */
+ public void collect(IExecutionDataOutput output, boolean reset);
+
+ /**
+ * Resets all coverage information. This method must only be called between
+ * {@link #startup()} and {@link #shutdown()}.
+ */
+ public void reset();
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/SystemPropertiesRuntime.java b/org.jacoco.core/src/org/jacoco/core/runtime/SystemPropertiesRuntime.java
new file mode 100644
index 0000000..a0948d8
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/runtime/SystemPropertiesRuntime.java
@@ -0,0 +1,126 @@
+package org.jacoco.core.runtime;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jacoco.core.data.IExecutionDataOutput;
+import org.jacoco.core.instr.GeneratorConstants;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.GeneratorAdapter;
+
+/**
+ * This {@link IRuntime} implementation places the coverage data in the
+ * {@link System#getProperties()} hash table. The advantage is, that the
+ * instrumented classes do not get dependencies to other classes than the JRE
+ * library itself.
+ *
+ * This runtime may cause problems in environments with security restrictions,
+ * in applications that replace the system properties or in applications that
+ * fail if non-String values are placed in the system properties.
+ *
+ *
+ * @author Marc R. Hoffmann
+ * @version $Revision: $
+ */
+public class SystemPropertiesRuntime implements IRuntime {
+
+ private static final String KEYPREFIX = "jacoco-";
+
+ private final String key;
+
+ /**
+ * Creates a new runtime with the given id. The id helps to separate
+ * different runtime instances. The instrumentation and the target VM must
+ * be based on a runtime with the same id.
+ *
+ * @param id
+ * Identifier for the runtime
+ */
+ public SystemPropertiesRuntime(int id) {
+ this.key = KEYPREFIX + Integer.toHexString(id);
+ }
+
+ /**
+ * Creates a new runtime with a random identifier.
+ */
+ public SystemPropertiesRuntime() {
+ this.key = KEYPREFIX + hashCode();
+ }
+
+ // TODO: lokale Variable vermeiden (swap!)
+ public void generateRegistration(long classId, GeneratorAdapter gen) {
+
+ // boolean[][] data = pop()
+ final int data = gen.newLocal(GeneratorConstants.DATAFIELD_TYPE);
+ gen.storeLocal(data);
+
+ // Properties stack := System.getProperties()
+ gen.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System",
+ "getProperties", "()Ljava/util/Properties;");
+
+ // gen.swap();
+
+ // Map stack := stack.get(key)
+ gen.push(key);
+ gen.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/Properties",
+ "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
+ gen.visitTypeInsn(Opcodes.CHECKCAST, "java/util/Map");
+
+ // stack.put(classId, data)
+ gen.push(classId);
+ gen.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf",
+ "(J)Ljava/lang/Long;");
+ // gen.swap();
+ gen.loadLocal(data);
+ gen.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ gen.pop();
+ }
+
+ public void startup() {
+ final Map<Long, boolean[][]> dataMap = Collections
+ .synchronizedMap(new HashMap<Long, boolean[][]>());
+ System.getProperties().put(key, dataMap);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map<Long, boolean[][]> getDataMap() {
+ final Object object = System.getProperties().get(key);
+ if (object == null) {
+ throw new IllegalStateException("Runtime not started.");
+ }
+ return (Map<Long, boolean[][]>) object;
+ }
+
+ public void collect(IExecutionDataOutput output, boolean reset) {
+ final Map<Long, boolean[][]> dataMap = getDataMap();
+ synchronized (dataMap) {
+ for (Map.Entry<Long, boolean[][]> entry : dataMap.entrySet()) {
+ final long classId = entry.getKey().longValue();
+ final boolean[][] blockData = entry.getValue();
+ output.classExecution(classId, blockData);
+ }
+ if (reset) {
+ reset();
+ }
+ }
+ }
+
+ public void reset() {
+ final Map<Long, boolean[][]> dataMap = getDataMap();
+ synchronized (dataMap) {
+ for (boolean[][] data : dataMap.values()) {
+ for (boolean[] arr : data) {
+ Arrays.fill(arr, false);
+ }
+ }
+ }
+ }
+
+ public void shutdown() {
+ System.getProperties().remove(key);
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/package.html b/org.jacoco.core/src/org/jacoco/core/runtime/package.html
new file mode 100644
index 0000000..274480e
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/runtime/package.html
@@ -0,0 +1,9 @@
+<html>
+<body>
+
+<p>
+ This package contains the classes to collect execution data at runtime.
+</p>
+
+</body>
+</html>
diff --git a/org.jacoco.core/src/overview.html b/org.jacoco.core/src/overview.html
new file mode 100644
index 0000000..370fd29
--- /dev/null
+++ b/org.jacoco.core/src/overview.html
@@ -0,0 +1,31 @@
+<html>
+<body>
+
+<p>
+ The JaCoCo core bundle implements the code coverage technology itself. It
+ provides APIs and implementation for
+</p>
+
+<ul>
+ <li>class file analysis and instrumentation</li>
+ <li>collecting execution data at runtime and</li>
+ <li>calculating and storing coverage data.</li>
+</ul>
+
+<pre>
+
+ | Class files | Runtime | Output Coverage Report
+
+
+ Analysis ----------------------------- IStructureDataOutput \
+ +---- Coverage Data
+ Instrumentation --- ExecutionData ---- IExecutionDataOutput /
+
+
+</pre>
+
+
+
+
+</body>
+</html>
\ No newline at end of file