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