Analyzer.java
1/*******************************************************************************
2 * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
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 static java.lang.String.format;
16
17import java.io.File;
18import java.io.FileInputStream;
19import java.io.IOException;
20import java.io.InputStream;
21import java.util.StringTokenizer;
22import java.util.zip.ZipEntry;
23import java.util.zip.ZipInputStream;
24
25import org.jacoco.core.data.IClassStructureVisitor;
26import org.jacoco.core.data.IStructureVisitor;
27import org.objectweb.asm.ClassReader;
28import org.objectweb.asm.ClassVisitor;
29import org.objectweb.asm.Opcodes;
30
31/**
32 * Several APIs to analyze class structures.
33 *
34 * @author Marc R. Hoffmann
35 * @version $Revision: $
36 */
37public class Analyzer {
38
39 private final IStructureVisitor structureVisitor;
40
41 /**
42 * Creates a new analyzer reporting to the given output.
43 *
44 * @param structureVisitor
45 * the output instance that will receive all structure data
46 */
47 public Analyzer(final IStructureVisitor structureVisitor) {
48 this.structureVisitor = structureVisitor;
49 }
50
51 /**
52 * Creates an ASM class visitor for analysis.
53 *
54 * @param classid
55 * id of the class calculated with {@link CRC64}
56 * @param classname
57 * VM name of the class
58 * @return ASM visitor to write class definition to
59 */
60 public ClassVisitor createAnalyzingVisitor(final long classid,
61 final String classname) {
62 final IClassStructureVisitor classStructure = structureVisitor
63 .visitClassStructure(classid, classname);
64 return new ClassAnalyzer(classStructure);
65 }
66
67 /**
68 * Analyzes the class given as a ASM reader.
69 *
70 * @param reader
71 * reader with class definitions
72 */
73 public void analyze(final ClassReader reader) {
74 if ((reader.getAccess() & Opcodes.ACC_INTERFACE) != 0) {
75 return;
76 }
77
78 final ClassVisitor visitor = createAnalyzingVisitor(CRC64
79 .checksum(reader.b), reader.getClassName());
80 reader.accept(visitor, 0);
81 }
82
83 /**
84 * Analyzes the class definition from a given in-memory buffer.
85 *
86 * @param buffer
87 * class definitions
88 */
89 public void analyze(final byte[] buffer) {
90 analyze(new ClassReader(buffer));
91 }
92
93 /**
94 * Analyzes the class definition from a given input stream.
95 *
96 * @param input
97 * stream to read class definition from
98 * @throws IOException
99 */
100 public void analyze(final InputStream input) throws IOException {
101 analyze(new ClassReader(input));
102 }
103
104 /**
105 * Analyzes the class definition contained in a given file.
106 *
107 * @param file
108 * class file
109 * @throws IOException
110 */
111 public void analyze(final File file) throws IOException {
112 final InputStream in = new FileInputStream(file);
113 analyze(new ClassReader(in));
114 in.close();
115 }
116
117 /**
118 * Analyzes all class files contained in the given directory and its
119 * children.
120 *
121 * @param directory
122 * folder to look for class files
123 * @throws IOException
124 * thrown if the given file object does not represent a readable
125 * directory
126 */
127 public void analyzeAll(final File directory) throws IOException {
128 final File[] files = directory.listFiles();
129 if (files == null) {
130 throw new IOException(format("Can't read directory %s.", directory));
131 }
132 for (final File f : files) {
133 if (f.isDirectory()) {
134 analyzeAll(f);
135 continue;
136 }
137 if (f.getName().endsWith(".class")) {
138 analyze(f);
139 }
140 }
141 }
142
143 /**
144 * Analyzes all class files contained in a JAR file.
145 *
146 * @param input
147 * stream to read the JAR file from
148 * @throws IOException
149 */
150 public void analyzeJAR(final InputStream input) throws IOException {
151 final ZipInputStream zip = new ZipInputStream(input);
152 while (true) {
153 final ZipEntry entry = zip.getNextEntry();
154 if (entry == null) {
155 break;
156 }
157 if (entry.getName().endsWith(".class")) {
158 analyze(zip);
159 }
160 }
161 }
162
163 /**
164 * Analyzes all class files contained in a JAR file.
165 *
166 * @param jarfile
167 * JAR file
168 * @throws IOException
169 */
170 public void analyzeJAR(final File jarfile) throws IOException {
171 final InputStream in = new FileInputStream(jarfile);
172 analyzeJAR(in);
173 in.close();
174 }
175
176 /**
177 * Analyzes all class from the given class path.
178 *
179 * @param path
180 * path definition
181 * @param basedir
182 * optional base directory, if <code>null</code> the current
183 * working directory is used as the base for relative path
184 * entries
185 * @throws IOException
186 */
187 public void analyzePath(final String path, final File basedir)
188 throws IOException {
189 final StringTokenizer tokenizer = new StringTokenizer(path,
190 File.pathSeparator);
191 while (tokenizer.hasMoreTokens()) {
192 final File entry = new File(basedir, tokenizer.nextToken());
193 if (entry.isDirectory()) {
194 analyzeAll(entry);
195 continue;
196 }
197 if (entry.isFile() && entry.getName().endsWith(".jar")) {
198 analyzeJAR(entry);
199 }
200 }
201 }
202
203}