Java tutorial
/* * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tdk.jcov.report; import com.sun.tdk.jcov.data.Scale; import com.sun.tdk.jcov.filter.MemberFilter; import com.sun.tdk.jcov.instrument.DataClass; import com.sun.tdk.jcov.instrument.DataField; import com.sun.tdk.jcov.instrument.DataMethod; import com.sun.tdk.jcov.report.javap.JavapClass; import com.sun.tdk.jcov.util.Utils; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.objectweb.asm.Opcodes; /** * <p> This class provides access to class coverage information. Sums of * underlying coverage information can be obtained by * <code>getData(DataType)</code> method using DataType.CLASS, DataType.METHOD, * DataType.FIELD, DataType.BLOCK, DataType.BRANCH, DataType.LINE. </p> <p> To * access specific method/field information use * <code>getMethodCoverageList()</code> and * <code>getFieldCoverageList()</code> methods </p> * * @see ProductCoverage * @see DataType * @see CoverageData * * @author Dmitry Fazunenko * @author Alexey Fedorchenko */ public class ClassCoverage extends AbstractCoverage { private String source; private boolean javapSource = false; private JavapClass javapClass; private List<MethodCoverage> methods = new ArrayList<MethodCoverage>(); private List<FieldCoverage> fields = new ArrayList<FieldCoverage>(); private LineCoverage lineCoverage = new LineCoverage(); private DataType[] supportedColumns = { DataType.CLASS, DataType.METHOD, DataType.FIELD, DataType.BLOCK, DataType.BRANCH, DataType.LINE }; private int access; private String fullname; private String name; private String packagename; /** * <p> Creates new ClassCoverage instance. </p> * * @param clz DataClass to read data from * @param srcRootPaths Paths for sources * @param filter Allows to filter read data */ public ClassCoverage(DataClass clz, String srcRootPaths[], MemberFilter filter) { this(clz, srcRootPaths, null, filter); } public ClassCoverage(DataClass clz, String srcRootPaths[], List<JavapClass> javapClasses, MemberFilter filter) { this(clz, srcRootPaths, null, filter, false); } public ClassCoverage(DataClass clz, String srcRootPaths[], List<JavapClass> javapClasses, MemberFilter filter, boolean anonym) { access = clz.getAccess(); fullname = clz.getFullname(); name = clz.getName(); packagename = clz.getPackageName(); for (DataMethod method : clz.getMethods()) { if (filter != null && !filter.accept(clz, method)) { continue; } MethodCoverage methodCoverage = new MethodCoverage(method); methodCoverage.setAnonymOn(anonym); if (method.getName() != null && method.getName().matches("\\$\\d.*")) { methodCoverage.setInAnonymClass(true); } methods.add(methodCoverage); lineCoverage.processLineCoverage(methodCoverage.getLineCoverage()); } for (DataField field : clz.getFields()) { if (filter != null && !filter.accept(clz, field)) { continue; } FieldCoverage fieldCoverage = new FieldCoverage(field); fields.add(fieldCoverage); } if (javapClasses == null) { this.source = findBestSource(clz, srcRootPaths); } else { javapSource = true; for (JavapClass jpClass : javapClasses) { if (jpClass != null && jpClass.getClassName() != null && (jpClass.getClassName()).equals(name)) { javapClass = jpClass; break; } } } Collections.sort(methods); Collections.sort(fields); } /** * @return true if the class doesn't contain neither methods or fields */ public boolean isEmpty() { return methods.isEmpty() && fields.isEmpty(); } /** * <p> Use getAccess() method to check for more specific modifiers. * getAccess() method returns a bit-mask of org.objectweb.asm.Opcodes * constants. </p> * * @return true if class access modifiers are <b>public</b> or * <b>protected</b> * @see ClassCoverage#getAccess() */ public boolean isPublicAPI() { return (access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0; } /** * <p> Use getAccess() method to check for more specific modifiers. * getAccess() method returns a bit-mask of org.objectweb.asm.Opcodes * constants. </p> * * @return true if class is <b>public</b> or <b>protected</b> * @see ClassCoverage#getAccess() */ public boolean isPublic() { return (access & Opcodes.ACC_PUBLIC) != 0; } /** * <p> Use getAccess() method to check for more specific modifiers. * getAccess() method returns a bit-mask of org.objectweb.asm.Opcodes * constants. </p> * * @return true if class is <b>public</b> * @see ClassCoverage#getAccess() */ public boolean isPrivate() { return (access & Opcodes.ACC_PRIVATE) != 0; } /** * <p> Use getAccess() method to check for more specific modifiers. * getAccess() method returns a bit-mask of org.objectweb.asm.Opcodes * constants. </p> * * @return true if class is <b>protected</b> * @see ClassCoverage#getAccess() */ public boolean isProtected() { return (access & Opcodes.ACC_PROTECTED) != 0; } /** * <p> Use getAccess() method to check for more specific modifiers. * getAccess() method returns a bit-mask of org.objectweb.asm.Opcodes * constants. </p> * * @return true if class is <b>abstract</b> * @see ClassCoverage#getAccess() */ public boolean isAbstract() { return (access & Opcodes.ACC_ABSTRACT) != 0; } /** * <p> Use getAccess() method to check for more specific modifiers. * getAccess() method returns a bit-mask of org.objectweb.asm.Opcodes * constants. </p> * * @return true if class is <b>final</b> * @see ClassCoverage#getAccess() */ public boolean isFinal() { return (access & Opcodes.ACC_FINAL) != 0; } /** * <p> Use this method to check for specific modifiers. </p> * * @return Access bit-mask of org.objectweb.asm.Opcodes constants. * @see Opcodes */ public int getAccess() { return access; } /** * @param test Number of a test in the testlist * @return true when any method in this class is marked as hit by the * <b>test</b> bit in scales * @see Scale */ public boolean isCoveredByTest(int test) { for (MethodCoverage method : methods) { Scale s = method.getScale(); if (s != null && s.isBitSet(test)) { return true; } } return false; } /** * @return classname in VM notation */ public String getName() { return name; } /** * @return getData(DataType.CLASS_COVERED) > 0; */ public boolean isCovered() { for (MethodCoverage method : methods) { if (method.count > 0) { return true; } } return false; } /** * @return methods in this class */ public List<MethodCoverage> getMethods() { return methods; } /** * @return fields in this class */ public List<FieldCoverage> getFields() { return fields; } /** * @return package name in VM notation */ public String getPackageName() { return packagename; } /** * @return canonical classname */ public String getFullClassName() { return fullname.replace('/', '.'); } /** * @return classname in VM notation ('/' as package separators) */ public String getFullClassNameFilename() { return fullname; } /** * Get class sourcefile * * @return class sourcefile */ public String getSource() { return source; } public boolean isJavapSource() { return javapSource; } public JavapClass getJavapClass() { return javapClass; } /** * Coverage kind (used in HTML reports as header for label) * * @return DataType.CLASS */ public DataType getDataType() { return DataType.CLASS; } protected DataType[] getDataTypes() { return supportedColumns; } /** * Returns true if passed line is covered, false otherwise. * * @param lineNum line number * @return true if passed line is covered, false otherwise. */ public boolean isLineCovered(int lineNum) { return lineCoverage.isLineCovered(lineNum); } /** * Returns true if the line with the given number contains java code * * @param lineNum line number * @return true if the line with the given number contains java code */ public boolean isCode(long lineNum) { return lineCoverage.isCode(lineNum); } /** * <p> Allows to get sums over the coverage of this class. E.g. * getData(DataType.METHOD) will return coverage data containing the total * number of methods in this class and number of covered methods in this * class. </p> <p> Allows to sum though CLASS, METHOD, FIELD, BLOCK, BRANCH * and LINE types </p> * * @param column Type to sum * @return CoverageData representing 2 fields - total number of members and * number of covered members * @see DataType * @see CoverageData */ public CoverageData getData(DataType column, int testNumber) { switch (column) { case CLASS: for (MethodCoverage method : methods) { if (method.count > 0 && (testNumber < 0 || method.isCoveredByTest(testNumber))) { return new CoverageData(1, 1); } } return new CoverageData(0, 1); case METHOD: case BLOCK: case BRANCH: CoverageData covered = new CoverageData(0, 0); for (MethodCoverage method : methods) { if (testNumber < 0 || method.isCoveredByTest(testNumber)) { covered.add(method.getData(column, testNumber)); } else { covered.add(new CoverageData(0, method.getData(column, testNumber).getTotal())); } } return covered; case FIELD: covered = new CoverageData(0, 0); for (FieldCoverage field : fields) { covered.add(field.getData(column)); } return covered; case LINE: return new CoverageData(lineCoverage.getCovered(), lineCoverage.getTotal()); default: return new CoverageData(); } } public CoverageData getData(DataType column) { return getData(column, -1); } /** * Finds a source that is the most acceptable for this DataClass * * @param clz * @param source_paths * @return */ private static String findBestSource(DataClass clz, String[] source_paths) { if (source_paths == null) { return clz.getSource(); } final char sep = File.separatorChar; String source_name = clz.getSource(); boolean dummy = false; if (source_name == null) { String clzName = clz.getName(); int indexOf = clzName.indexOf('$'); if (indexOf > 0) { clzName = clzName.substring(0, indexOf); } else { clzName = clzName + ".java"; } source_name = clzName; dummy = true; } if (source_name.startsWith(pref) && source_name.endsWith(suff)) { source_name = source_name.substring(0, source_name.length() - suff.length()); source_name = source_name.substring(pref.length()); dummy = true; } source_name = source_name.replace('/', sep); source_name = source_name.replace('\\', sep); source_name = Utils.basename(source_name); if (dummy) { int i = source_name.indexOf("$"); if (i > 0) { source_name = source_name.substring(0, i) + ".java"; } } String pckg = clz.getPackageName(); source_name = pckg.replace('/', File.separatorChar) + sep + source_name; for (int i = 0; i < source_paths.length; i++) { File f = new File(source_paths[i] + source_name); if (f.exists()) { return f.getAbsolutePath(); } } return clz.getSource(); // not found } static final String pref = "<UNKNOWN_SOURCE/"; static final String suff = ">"; }