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 * @return ASM visitor to write class definition to
57 */
58 public ClassVisitor createAnalyzingVisitor(final long classid) {
59 final IClassStructureVisitor classStructure = structureVisitor
60 .visitClassStructure(classid);
61 return new ClassAnalyzer(classStructure);
62 }
63
64 /**
65 * Analyzes the class given as a ASM reader.
66 *
67 * @param reader
68 * reader with class definitions
69 */
70 public void analyze(final ClassReader reader) {
71 if ((reader.getAccess() & Opcodes.ACC_INTERFACE) != 0) {
72 return;
73 }
74
75 final ClassVisitor visitor = createAnalyzingVisitor(CRC64
76 .checksum(reader.b));
77 reader.accept(visitor, 0);
78 }
79
80 /**
81 * Analyzes the class definition from a given in-memory buffer.
82 *
83 * @param buffer
84 * class definitions
85 */
86 public void analyze(final byte[] buffer) {
87 analyze(new ClassReader(buffer));
88 }
89
90 /**
91 * Analyzes the class definition from a given input stream.
92 *
93 * @param input
94 * stream to read class definition from
95 * @throws IOException
96 */
97 public void analyze(final InputStream input) throws IOException {
98 analyze(new ClassReader(input));
99 }
100
101 /**
102 * Analyzes the class definition contained in a given file.
103 *
104 * @param file
105 * class file
106 * @throws IOException
107 */
108 public void analyze(final File file) throws IOException {
109 final InputStream in = new FileInputStream(file);
110 analyze(new ClassReader(in));
111 in.close();
112 }
113
114 /**
115 * Analyzes all class files contained in the given directory and its
116 * children.
117 *
118 * @param directory
119 * folder to look for class files
120 * @throws IOException
121 * thrown if the given file object does not represent a readable
122 * directory
123 */
124 public void analyzeAll(final File directory) throws IOException {
125 final File[] files = directory.listFiles();
126 if (files == null) {
127 throw new IOException(format("Can't read directory %s.", directory));
128 }
129 for (final File f : files) {
130 if (f.isDirectory()) {
131 analyzeAll(f);
132 continue;
133 }
134 if (f.getName().endsWith(".class")) {
135 analyze(f);
136 }
137 }
138 }
139
140 /**
141 * Analyzes all class files contained in a JAR file.
142 *
143 * @param input
144 * stream to read the JAR file from
145 * @throws IOException
146 */
147 public void analyzeJAR(final InputStream input) throws IOException {
148 final ZipInputStream zip = new ZipInputStream(input);
149 while (true) {
150 final ZipEntry entry = zip.getNextEntry();
151 if (entry == null) {
152 break;
153 }
154 if (entry.getName().endsWith(".class")) {
155 analyze(zip);
156 }
157 }
158 }
159
160 /**
161 * Analyzes all class files contained in a JAR file.
162 *
163 * @param jarfile
164 * JAR file
165 * @throws IOException
166 */
167 public void analyzeJAR(final File jarfile) throws IOException {
168 final InputStream in = new FileInputStream(jarfile);
169 analyzeJAR(in);
170 in.close();
171 }
172
173 /**
174 * Analyzes all class from the given class path.
175 *
176 * @param path
177 * path definition
178 * @param basedir
179 * optional base directory, if <code>null</code> the current
180 * working directory is used as the base for relative path
181 * entries
182 * @throws IOException
183 */
184 public void analyzePath(final String path, final File basedir)
185 throws IOException {
186 final StringTokenizer tokenizer = new StringTokenizer(path,
187 File.pathSeparator);
188 while (tokenizer.hasMoreTokens()) {
189 final File entry = new File(basedir, tokenizer.nextToken());
190 if (entry.isDirectory()) {
191 analyzeAll(entry);
192 continue;
193 }
194 if (entry.isFile() && entry.getName().endsWith(".jar")) {
195 analyzeJAR(entry);
196 }
197 }
198 }
199
200}