1e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov/*******************************************************************************
2b9d1b54e300318b470d9fedccc69d75187016444Evgeny Mandrikov * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors
3e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * All rights reserved. This program and the accompanying materials
4e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * are made available under the terms of the Eclipse Public License v1.0
5e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * which accompanies this distribution, and is available at
6e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * http://www.eclipse.org/legal/epl-v10.html
7e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov *
8e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Contributors:
9e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov *    Marc R. Hoffmann - initial API and implementation
10e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov *
11e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov *******************************************************************************/
12e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovpackage org.jacoco.core.analysis;
13e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
14e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.io.File;
15e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.io.FileInputStream;
16e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.io.IOException;
17e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.io.InputStream;
18e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.util.StringTokenizer;
19fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmannimport java.util.zip.GZIPInputStream;
20ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmannimport java.util.zip.ZipEntry;
21e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.util.zip.ZipInputStream;
22e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
23e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.data.ExecutionData;
24e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.data.ExecutionDataStore;
25adf2bf660e12b3b284eb80965328802101cf6762Marc R. Hoffmannimport org.jacoco.core.internal.ContentTypeDetector;
26caa820ed62133f47bacba06ea931bf5d7c43dcd6Roberto Araujoimport org.jacoco.core.internal.InputStreams;
27fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmannimport org.jacoco.core.internal.Pack200Streams;
28e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.internal.analysis.ClassAnalyzer;
29f4622217085198f3ae42906934026961b29d1111Marc R. Hoffmannimport org.jacoco.core.internal.analysis.ClassCoverageImpl;
30e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.internal.analysis.StringPool;
31e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.internal.data.CRC64;
32e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.internal.flow.ClassProbesAdapter;
33e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.objectweb.asm.ClassReader;
34e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.objectweb.asm.ClassVisitor;
35e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
36e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov/**
37e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * An {@link Analyzer} instance processes a set of Java class files and
38e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * calculates coverage data for them. For each class file the result is reported
39e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * to a given {@link ICoverageVisitor} instance. In addition the
40e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * {@link Analyzer} requires a {@link ExecutionDataStore} instance that holds
41e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * the execution data for the classes to analyze. The {@link Analyzer} offers
42e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * several methods to analyze classes from a variety of sources.
43e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */
44e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovpublic class Analyzer {
45e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
46e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final ExecutionDataStore executionData;
47e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
48e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final ICoverageVisitor coverageVisitor;
49e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
50e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final StringPool stringPool;
51e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
52e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
53e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * Creates a new analyzer reporting to the given output.
54e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
55e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param executionData
56e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            execution data
57e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param coverageVisitor
58e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            the output instance that will coverage data for every analyzed
59e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            class
60e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
61e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public Analyzer(final ExecutionDataStore executionData,
62e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final ICoverageVisitor coverageVisitor) {
63e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		this.executionData = executionData;
64e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		this.coverageVisitor = coverageVisitor;
65e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		this.stringPool = new StringPool();
66e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
67e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
68e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
69e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * Creates an ASM class visitor for analysis.
70e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
71e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param classid
72e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            id of the class calculated with {@link CRC64}
7360c33d63e75c1b0a5da9bd58be1f0b5434240220Marc R. Hoffmann	 * @param className
7460c33d63e75c1b0a5da9bd58be1f0b5434240220Marc R. Hoffmann	 *            VM name of the class
75e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @return ASM visitor to write class definition to
76e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
7760c33d63e75c1b0a5da9bd58be1f0b5434240220Marc R. Hoffmann	private ClassVisitor createAnalyzingVisitor(final long classid,
7860c33d63e75c1b0a5da9bd58be1f0b5434240220Marc R. Hoffmann			final String className) {
79e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		final ExecutionData data = executionData.get(classid);
8060c33d63e75c1b0a5da9bd58be1f0b5434240220Marc R. Hoffmann		final boolean[] probes;
8160c33d63e75c1b0a5da9bd58be1f0b5434240220Marc R. Hoffmann		final boolean noMatch;
8260c33d63e75c1b0a5da9bd58be1f0b5434240220Marc R. Hoffmann		if (data == null) {
8360c33d63e75c1b0a5da9bd58be1f0b5434240220Marc R. Hoffmann			probes = null;
8460c33d63e75c1b0a5da9bd58be1f0b5434240220Marc R. Hoffmann			noMatch = executionData.contains(className);
8560c33d63e75c1b0a5da9bd58be1f0b5434240220Marc R. Hoffmann		} else {
8660c33d63e75c1b0a5da9bd58be1f0b5434240220Marc R. Hoffmann			probes = data.getProbes();
8760c33d63e75c1b0a5da9bd58be1f0b5434240220Marc R. Hoffmann			noMatch = false;
8860c33d63e75c1b0a5da9bd58be1f0b5434240220Marc R. Hoffmann		}
89f4622217085198f3ae42906934026961b29d1111Marc R. Hoffmann		final ClassCoverageImpl coverage = new ClassCoverageImpl(className,
90f4622217085198f3ae42906934026961b29d1111Marc R. Hoffmann				classid, noMatch);
91f4622217085198f3ae42906934026961b29d1111Marc R. Hoffmann		final ClassAnalyzer analyzer = new ClassAnalyzer(coverage, probes,
92f4622217085198f3ae42906934026961b29d1111Marc R. Hoffmann				stringPool) {
93e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			@Override
94e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			public void visitEnd() {
95e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				super.visitEnd();
96f4622217085198f3ae42906934026961b29d1111Marc R. Hoffmann				coverageVisitor.visitCoverage(coverage);
97e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			}
98e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		};
998f81097f70a2b9c570e225dec4dcf3a28b988efcMarc R. Hoffmann		return new ClassProbesAdapter(analyzer, false);
100e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
101e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
102e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
103e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * Analyzes the class given as a ASM reader.
104e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
105e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param reader
106e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            reader with class definitions
107e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
108e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public void analyzeClass(final ClassReader reader) {
10960c33d63e75c1b0a5da9bd58be1f0b5434240220Marc R. Hoffmann		final ClassVisitor visitor = createAnalyzingVisitor(
1100c50b210738a2d3facb53598a7208d456f214364Evgeny Mandrikov				CRC64.classId(reader.b), reader.getClassName());
111e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		reader.accept(visitor, 0);
112e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
113e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
114e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
115e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * Analyzes the class definition from a given in-memory buffer.
116e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
117e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param buffer
118e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            class definitions
119024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann	 * @param location
120024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann	 *            a location description used for exception messages
121ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann	 * @throws IOException
122ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann	 *             if the class can't be analyzed
123e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
124024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann	public void analyzeClass(final byte[] buffer, final String location)
125ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann			throws IOException {
126ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann		try {
127caa820ed62133f47bacba06ea931bf5d7c43dcd6Roberto Araujo			analyzeClass(new ClassReader(buffer));
128ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann		} catch (final RuntimeException cause) {
129024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann			throw analyzerError(location, cause);
130ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann		}
131e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
132e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
133e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
13411ac066394b176096f48516d8c6b486648ba24c6Marc R. Hoffmann	 * Analyzes the class definition from a given input stream. The provided
13511ac066394b176096f48516d8c6b486648ba24c6Marc R. Hoffmann	 * {@link InputStream} is not closed by this method.
136e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
137e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param input
138e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            stream to read class definition from
139024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann	 * @param location
140024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann	 *            a location description used for exception messages
141e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @throws IOException
142ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann	 *             if the stream can't be read or the class can't be analyzed
143e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
144024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann	public void analyzeClass(final InputStream input, final String location)
145ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann			throws IOException {
1461b959434976cccd90c8504a6f8a721151f5ff8afEvgeny Mandrikov		final byte[] buffer;
147ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann		try {
148caa820ed62133f47bacba06ea931bf5d7c43dcd6Roberto Araujo			buffer = InputStreams.readFully(input);
1491b959434976cccd90c8504a6f8a721151f5ff8afEvgeny Mandrikov		} catch (final IOException e) {
150024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann			throw analyzerError(location, e);
151ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann		}
1521b959434976cccd90c8504a6f8a721151f5ff8afEvgeny Mandrikov		analyzeClass(buffer, location);
153e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
154e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
155024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann	private IOException analyzerError(final String location,
156fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov			final Exception cause) {
15711ac066394b176096f48516d8c6b486648ba24c6Marc R. Hoffmann		final IOException ex = new IOException(
15811ac066394b176096f48516d8c6b486648ba24c6Marc R. Hoffmann				String.format("Error while analyzing %s.", location));
159ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann		ex.initCause(cause);
160ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann		return ex;
161e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
162e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
163e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
164e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * Analyzes all classes found in the given input stream. The input stream
165fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmann	 * may either represent a single class file, a ZIP archive, a Pack200
166fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmann	 * archive or a gzip stream that is searched recursively for class files.
16711ac066394b176096f48516d8c6b486648ba24c6Marc R. Hoffmann	 * All other content types are ignored. The provided {@link InputStream} is
16811ac066394b176096f48516d8c6b486648ba24c6Marc R. Hoffmann	 * not closed by this method.
169e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
170e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param input
171e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            input data
172024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann	 * @param location
173024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann	 *            a location description used for exception messages
174e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @return number of class files found
175e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @throws IOException
176ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann	 *             if the stream can't be read or a class can't be analyzed
177e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
178024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann	public int analyzeAll(final InputStream input, final String location)
179ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann			throws IOException {
180fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		final ContentTypeDetector detector;
181fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		try {
182fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov			detector = new ContentTypeDetector(input);
18311ac066394b176096f48516d8c6b486648ba24c6Marc R. Hoffmann		} catch (final IOException e) {
184fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov			throw analyzerError(location, e);
185fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		}
186e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		switch (detector.getType()) {
187e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		case ContentTypeDetector.CLASSFILE:
188024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann			analyzeClass(detector.getInputStream(), location);
189e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			return 1;
190e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		case ContentTypeDetector.ZIPFILE:
191024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann			return analyzeZip(detector.getInputStream(), location);
192fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmann		case ContentTypeDetector.GZFILE:
193024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann			return analyzeGzip(detector.getInputStream(), location);
194fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmann		case ContentTypeDetector.PACK200FILE:
195024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann			return analyzePack200(detector.getInputStream(), location);
196e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		default:
197e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			return 0;
198e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		}
199e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
200e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
201e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
202e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * Analyzes all class files contained in the given file or folder. Class
203e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * files as well as ZIP files are considered. Folders are searched
204e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * recursively.
205e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
206e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param file
207e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            file or folder to look for class files
208e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @return number of class files found
209e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @throws IOException
210ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann	 *             if the file can't be read or a class can't be analyzed
211e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
212e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public int analyzeAll(final File file) throws IOException {
213e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		int count = 0;
214e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		if (file.isDirectory()) {
215e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			for (final File f : file.listFiles()) {
216e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				count += analyzeAll(f);
217e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			}
218e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		} else {
219e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final InputStream in = new FileInputStream(file);
220e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			try {
221ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann				count += analyzeAll(in, file.getPath());
222e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			} finally {
223e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				in.close();
224e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			}
225e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		}
226e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		return count;
227e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
228e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
229e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
230e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * Analyzes all classes from the given class path. Directories containing
231e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * class files as well as archive files are considered.
232e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
233e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param path
234e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            path definition
235e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param basedir
236e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            optional base directory, if <code>null</code> the current
237e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            working directory is used as the base for relative path
238e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            entries
239e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @return number of class files found
240e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @throws IOException
241ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann	 *             if a file can't be read or a class can't be analyzed
242e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
243e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public int analyzeAll(final String path, final File basedir)
244e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			throws IOException {
245e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		int count = 0;
246fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		final StringTokenizer st = new StringTokenizer(path,
247fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov				File.pathSeparator);
248e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		while (st.hasMoreTokens()) {
249e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			count += analyzeAll(new File(basedir, st.nextToken()));
250e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		}
251e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		return count;
252e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
253e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
254024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann	private int analyzeZip(final InputStream input, final String location)
255ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann			throws IOException {
256fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmann		final ZipInputStream zip = new ZipInputStream(input);
257ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann		ZipEntry entry;
258fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmann		int count = 0;
259fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		while ((entry = nextEntry(zip, location)) != null) {
260024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann			count += analyzeAll(zip, location + "@" + entry.getName());
261fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmann		}
262fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmann		return count;
263fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmann	}
264fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmann
26511ac066394b176096f48516d8c6b486648ba24c6Marc R. Hoffmann	private ZipEntry nextEntry(final ZipInputStream input,
26611ac066394b176096f48516d8c6b486648ba24c6Marc R. Hoffmann			final String location) throws IOException {
267fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		try {
268fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov			return input.getNextEntry();
26911ac066394b176096f48516d8c6b486648ba24c6Marc R. Hoffmann		} catch (final IOException e) {
270fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov			throw analyzerError(location, e);
271fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		}
272fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov	}
273fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov
274024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann	private int analyzeGzip(final InputStream input, final String location)
275ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann			throws IOException {
276fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		GZIPInputStream gzipInputStream;
277fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		try {
278fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov			gzipInputStream = new GZIPInputStream(input);
27911ac066394b176096f48516d8c6b486648ba24c6Marc R. Hoffmann		} catch (final IOException e) {
280fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov			throw analyzerError(location, e);
281fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		}
282fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		return analyzeAll(gzipInputStream, location);
283fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmann	}
284fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmann
285024536619572c1ad5274cbe6872ea8068a9498acMarc R. Hoffmann	private int analyzePack200(final InputStream input, final String location)
286ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann			throws IOException {
287fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		InputStream unpackedInput;
288fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		try {
289fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov			unpackedInput = Pack200Streams.unpack(input);
29011ac066394b176096f48516d8c6b486648ba24c6Marc R. Hoffmann		} catch (final IOException e) {
291fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov			throw analyzerError(location, e);
292fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		}
293fde254dd251e6afc1c8f1272efb1be2b6c767387Evgeny Mandrikov		return analyzeAll(unpackedInput, location);
294fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmann	}
295fc340a20c201bec9c0ee31ec16c85766477a1edfMarc R. Hoffmann
296e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov}
297