1/*******************************************************************************
2 * Copyright (c) 2009, 2015 Mountainminds GmbH & Co. KG and Contributors
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 *    Marc R. Hoffmann - initial API and implementation
10 *
11 *******************************************************************************/
12package org.jacoco.core.internal.analysis;
13
14import org.jacoco.core.analysis.IMethodCoverage;
15import org.jacoco.core.internal.flow.ClassProbesVisitor;
16import org.jacoco.core.internal.flow.MethodProbesVisitor;
17import org.jacoco.core.internal.instr.InstrSupport;
18import org.objectweb.asm.FieldVisitor;
19import org.objectweb.asm.Opcodes;
20
21/**
22 * Analyzes the structure of a class.
23 */
24public class ClassAnalyzer extends ClassProbesVisitor {
25
26	private final long classid;
27	private final boolean noMatch;
28	private final boolean[] probes;
29	private final StringPool stringPool;
30
31	private ClassCoverageImpl coverage;
32
33	/**
34	 * Creates a new analyzer that builds coverage data for a class.
35	 *
36	 * @param classid
37	 *            id of the class
38	 * @param noMatch
39	 *            <code>true</code> if class id does not match with execution
40	 *            data
41	 * @param probes
42	 *            execution data for this class or <code>null</code>
43	 * @param stringPool
44	 *            shared pool to minimize the number of {@link String} instances
45	 */
46	public ClassAnalyzer(final long classid, final boolean noMatch,
47			final boolean[] probes, final StringPool stringPool) {
48		this.classid = classid;
49		this.noMatch = noMatch;
50		this.probes = probes;
51		this.stringPool = stringPool;
52	}
53
54	/**
55	 * Returns the coverage data for this class after this visitor has been
56	 * processed.
57	 *
58	 * @return coverage data for this class
59	 */
60	public ClassCoverageImpl getCoverage() {
61		return coverage;
62	}
63
64	@Override
65	public void visit(final int version, final int access, final String name,
66			final String signature, final String superName,
67			final String[] interfaces) {
68		this.coverage = new ClassCoverageImpl(stringPool.get(name), classid,
69				noMatch, stringPool.get(signature), stringPool.get(superName),
70				stringPool.get(interfaces));
71	}
72
73	@Override
74	public void visitSource(final String source, final String debug) {
75		this.coverage.setSourceFileName(stringPool.get(source));
76	}
77
78	@Override
79	public MethodProbesVisitor visitMethod(final int access, final String name,
80			final String desc, final String signature, final String[] exceptions) {
81
82		InstrSupport.assertNotInstrumented(name, coverage.getName());
83
84		if (isMethodFiltered(access, name)) {
85			return null;
86		}
87
88		return new MethodAnalyzer(stringPool.get(name), stringPool.get(desc),
89				stringPool.get(signature), probes) {
90			@Override
91			public void visitEnd() {
92				super.visitEnd();
93				final IMethodCoverage methodCoverage = getCoverage();
94				if (methodCoverage.getInstructionCounter().getTotalCount() > 0) {
95					// Only consider methods that actually contain code
96					coverage.addMethod(methodCoverage);
97				}
98			}
99		};
100	}
101
102	// TODO: Use filter hook in future
103	private boolean isMethodFiltered(final int access, final String name) {
104		return (access & Opcodes.ACC_SYNTHETIC) != 0
105				&& !name.startsWith("lambda$");
106	}
107
108	@Override
109	public FieldVisitor visitField(final int access, final String name,
110			final String desc, final String signature, final Object value) {
111		InstrSupport.assertNotInstrumented(name, coverage.getName());
112		return super.visitField(access, name, desc, signature, value);
113	}
114
115	@Override
116	public void visitTotalProbeCount(final int count) {
117		// nothing to do
118	}
119
120}
121