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