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