1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.sun.tdk.jcov;
  26 
  27 import com.sun.tdk.jcov.instrument.DataMethod;
  28 import com.sun.tdk.jcov.instrument.DataField;
  29 import com.sun.tdk.jcov.instrument.DataClass;
  30 import com.sun.tdk.jcov.processing.DataProcessorSPI;
  31 import com.sun.tdk.jcov.report.AncFilter;
  32 import com.sun.tdk.jcov.report.ancfilters.DefaultAncFilter;
  33 import com.sun.tdk.jcov.util.Utils;
  34 import com.sun.tdk.jcov.data.FileFormatException;
  35 import com.sun.tdk.jcov.data.Result;
  36 import com.sun.tdk.jcov.instrument.DataRoot;
  37 import com.sun.tdk.jcov.processing.ProcessingException;
  38 
  39 import com.sun.tdk.jcov.tools.EnvHandler;
  40 import com.sun.tdk.jcov.tools.OptionDescr;
  41 import com.sun.tdk.jcov.io.Reader;
  42 import com.sun.tdk.jcov.filter.ConveyerFilter;
  43 import com.sun.tdk.jcov.filter.MemberFilter;
  44 import com.sun.tdk.jcov.filter.FilterFactory;
  45 import com.sun.tdk.jcov.instrument.InstrumentationOptions;
  46 import com.sun.tdk.jcov.io.ClassSignatureFilter;
  47 import com.sun.tdk.jcov.processing.DefaultDataProcessorSPI;
  48 import com.sun.tdk.jcov.processing.StubSpi;
  49 import com.sun.tdk.jcov.report.DefaultReportGeneratorSPI;
  50 import com.sun.tdk.jcov.report.ProductCoverage;
  51 import com.sun.tdk.jcov.report.ReportGenerator;
  52 import com.sun.tdk.jcov.report.ReportGeneratorSPI;
  53 import com.sun.tdk.jcov.report.SmartTestService;
  54 import com.sun.tdk.jcov.report.javap.JavapClass;
  55 import com.sun.tdk.jcov.report.javap.JavapRepGen;
  56 import com.sun.tdk.jcov.tools.JCovCMDTool;
  57 import com.sun.tdk.jcov.tools.SPIDescr;
  58 import java.io.IOException;
  59 import java.util.ArrayList;
  60 import java.util.logging.Level;
  61 import java.util.logging.Logger;
  62 
  63 import static com.sun.tdk.jcov.tools.OptionDescr.*;
  64 import java.io.File;
  65 import java.util.Arrays;
  66 import java.util.ServiceLoader;
  67 import java.util.List;
  68 import org.objectweb.asm.Opcodes;
  69 
  70 /**
  71  * <p> Report generation. </p>
  72  *
  73  * @author Andrey Titov
  74  * @author Dmitry Fazunenko
  75  * @author Alexey Fedorchenko
  76  */
  77 public class RepGen extends JCovCMDTool {
  78 
  79     final static String CUSTOM_REPORT_GENERATOR_SPI = "customreport.spi";
  80     final static String DATA_PROCESSOR_SPI = "dataprocessor.spi";
  81 
  82     // logger initialization
  83     static {
  84         Utils.initLogger();
  85         logger = Logger.getLogger(RepGen.class.getName());
  86     }
  87     private final static Logger logger;
  88     /**
  89      * Consider or not enums. true - means ignore enum classes.
  90      */
  91     private boolean noEnums = false;
  92     /**
  93      * Whether show or not field coverage in the report (false by default).
  94      */
  95     private boolean showFields = false;
  96     private ReportGeneratorSPI reportGeneratorSPIs[];
  97     private DataProcessorSPI dataProcessorSPIs[];
  98     private String[] include = new String[]{".*"};
  99     private String[] exclude = new String[]{""};
 100     private String[] m_include = new String[]{".*"};
 101     private String[] m_exclude = new String[]{""};
 102     private String[] fms = null;
 103     private String filter = null;
 104     private String[] ancfilters = null;
 105     private String[] ancdeffilters = null;
 106     private boolean noAbstract = false;
 107     private boolean syntheticOn = false;
 108     private boolean isPublicAPI = false;
 109     private String[] filenames;
 110     private String name;
 111     private String outputDir;
 112     private String testlist;
 113     private String srcRootPath;
 114     private boolean anonym = false;
 115     private boolean withTestsInfo = false;
 116     //path to the jar, dir or .class for javap repgen
 117     private String classesPath;
 118     private AncFilter[] ancfiltersClasses = null;
 119     private String mainReportTitle = null;
 120     private String overviewListTitle = null;
 121     private String entitiesTitle = null;
 122 
 123     public RepGen() {
 124         readPlugins = true;
 125     }
 126 
 127     /**
 128      * Generate report using default (html) report generator
 129      *
 130      * @param output
 131      * @param jcovResult
 132      * @throws ProcessingException
 133      * @throws FileFormatException
 134      * @throws Exception
 135      */
 136     public void generateReport(String output, Result jcovResult) throws ProcessingException, FileFormatException, Exception {
 137         generateReport(getDefaultReportGenerator(), output, jcovResult, null);
 138     }
 139 
 140     /**
 141      * Generate report using specified format
 142      *
 143      * @param format
 144      * @param output
 145      * @param jcovResult
 146      * @throws ProcessingException
 147      * @throws FileFormatException
 148      * @throws Exception
 149      */
 150     public void generateReport(String format, String output, Result jcovResult) throws ProcessingException, FileFormatException, Exception {
 151         generateReport(format, output, jcovResult, null);
 152     }
 153 
 154     /**
 155      * Generate report using specified format
 156      *
 157      * @param format
 158      * @param output
 159      * @param jcovResult
 160      * @param srcRootPath
 161      * @throws ProcessingException
 162      * @throws FileFormatException
 163      * @throws Exception
 164      */
 165     public void generateReport(String format, String output, Result jcovResult, String srcRootPath) throws ProcessingException, FileFormatException, Exception {
 166         ReportGenerator rg = null;
 167         if (format != null) {
 168             rg = findReportGenerator(format);
 169         } else {
 170             rg = getDefaultReportGenerator();
 171         }
 172         if (rg == null) {
 173             throw new Exception("Specified ReportGenerator name (" + format + ") was not found");
 174         }
 175 
 176         generateReport(rg, output, jcovResult, srcRootPath);
 177     }
 178 
 179     /**
 180      * Generate report using specified report generator
 181      *
 182      * @param rg
 183      * @param output
 184      * @param jcovResult
 185      * @param srcRootPath
 186      * @throws ProcessingException
 187      * @throws FileFormatException
 188      * @throws Exception
 189      */
 190     public void generateReport(ReportGenerator rg, String output, Result jcovResult, String srcRootPath) throws ProcessingException, FileFormatException, Exception {
 191         generateReport(rg, output, jcovResult, srcRootPath, null);
 192     }
 193 
 194     /**
 195      * Generate report using specified report generator
 196      *
 197      * @param rg
 198      * @param output
 199      * @param jcovResult
 200      * @param srcRootPath
 201      * @param classes parsed javap classes
 202      * @throws ProcessingException
 203      * @throws FileFormatException
 204      * @throws Exception
 205      */
 206     public void generateReport(ReportGenerator rg, String output, Result jcovResult, String srcRootPath, List<JavapClass> classes) throws ProcessingException, FileFormatException, Exception {
 207         try {
 208             logger.log(Level.INFO, "-- Writing report to {0}", output);
 209             rg.init(output);
 210             logger.fine("OK");
 211         } catch (Throwable ex) {
 212             logger.log(Level.SEVERE, "Error while reading output file by ReportGenerator " + rg.getClass().getName(), ex);
 213             return;
 214         }
 215 
 216         logger.log(Level.INFO, "-- Reading data from {0}", jcovResult.getResultPath());
 217         DataRoot file_image = readDataRootFile(jcovResult.getResultPath(), jcovResult.isTestListSet(), include, exclude, fms);
 218 
 219         if (!syntheticOn) {
 220             file_image.applyFilter(new ANC_FILTER());
 221         }
 222 
 223         MemberFilter customFilter = null;
 224         if (filter != null) {
 225             logger.fine("-- Initializing custom filter");
 226             customFilter = initCustomFilter(filter, null);
 227             logger.fine("OK");
 228         }
 229         if (customFilter != null) {
 230             logger.log(Level.INFO, "-- Applying filter {0}", customFilter.getClass().getName());
 231             file_image.applyFilter(customFilter);
 232             logger.fine("OK");
 233         }
 234 
 235         if (ancfilters != null){
 236             ancfiltersClasses = new AncFilter[ancfilters.length];
 237             for (int i = 0; i < ancfilters.length; i++) {
 238                 try {
 239                     String ancfilter = ancfilters[i];
 240                     Class ancFilteClass = Class.forName(ancfilter);
 241                     ancfiltersClasses[i] = (AncFilter) ancFilteClass.newInstance();
 242                 } catch (Exception e) {
 243                     throw new Error("Cannot create an instance of "
 244                             + "AncFilter: ", e);
 245                 }
 246             }
 247         }
 248 
 249         if (ancdeffilters != null) {
 250             ServiceLoader<DefaultAncFilter> loader = ServiceLoader.load(DefaultAncFilter.class);
 251             List<AncFilter> defaultANCFiltersList = new ArrayList<AncFilter>();
 252             if (ancfiltersClasses != null && ancfiltersClasses.length > 0){
 253                 defaultANCFiltersList.addAll(Arrays.asList(ancfiltersClasses));
 254             }
 255             if (ancdeffilters.length == 1 && ancdeffilters[0].equals("all")){
 256                 for (DefaultAncFilter filter : loader) {
 257                     defaultANCFiltersList.add(filter);
 258                 }
 259             }
 260             else {
 261                 for (String defaulAncFilter : ancdeffilters) {
 262                     boolean found = false;
 263                     for (DefaultAncFilter filter : loader) {
 264                         if (defaulAncFilter.equals(filter.getFilterName())) {
 265                             defaultANCFiltersList.add(filter);
 266                             found = true;
 267                             break;
 268                         }
 269                     }
 270                     if (!found) {
 271                         logger.log(Level.SEVERE, "There is no default ANC filter for \"{0}\" value", defaulAncFilter);
 272                     }
 273                 }
 274             }
 275             ancfiltersClasses = defaultANCFiltersList.toArray(new AncFilter[defaultANCFiltersList.size()]);
 276         }
 277 
 278         if (dataProcessorSPIs != null) {
 279             for (DataProcessorSPI spi : dataProcessorSPIs) {
 280                 logger.log(Level.INFO, "-- Applying data processor {0}", spi.getClass());
 281                 file_image = spi.getDataProcessor().process(file_image);
 282             }
 283         }
 284         logger.fine("OK");
 285 
 286         SmartTestService sts = null;
 287         if (jcovResult.isTestListSet()) {
 288             logger.fine("-- Initializing test list");
 289             sts = new SmartTestService(jcovResult.getTestList());
 290             if (file_image.getScaleOpts().getScaleSize() != sts.getTestCount()) {
 291                 logger.log(Level.SEVERE, "The sizes of tests in JCov file and in test list differ.\n"
 292                         + "Datafile {0} contains {1} item(s).\nThe test list contains {2} item(s).",
 293                         new Object[]{jcovResult.getResultPath(), file_image.getScaleOpts().getScaleSize(), sts.getTestCount()});
 294                 throw new Exception("The sizes of tests in JCov file and in test list differ");
 295             }
 296             logger.fine("OK");
 297         }
 298         ReportGenerator.Options options = new ReportGenerator.Options(srcRootPath, sts, classes, withTestsInfo, false,
 299                 mainReportTitle, overviewListTitle, entitiesTitle);
 300         options.setInstrMode(file_image.getParams().getMode());
 301         options.setAnonymOn(anonym);
 302 
 303         try {
 304             ProductCoverage coverage = new ProductCoverage(file_image, options.getSrcRootPaths(), options.getJavapClasses(), isPublicAPI, noAbstract, anonym, ancfiltersClasses);
 305 
 306             logger.log(Level.INFO, "- Starting ReportGenerator {0}", rg.getClass().getName());
 307             rg.generateReport(coverage, options);
 308         } catch (Throwable ex) {
 309             if (ex.getMessage() != null) {
 310                 throw new Exception("ReportGenerator produced exception " + ex.getMessage(), ex);
 311             } else {
 312                 throw new Exception("ReportGenerator produced exception " + ex, ex);
 313             }
 314         }
 315 
 316         logger.log(Level.INFO, "- Report generation done");
 317         return;
 318     }
 319 
 320     /**
 321      * Get default (html) report generator
 322      *
 323      * @return default (html) report generator
 324      */
 325     public ReportGenerator getDefaultReportGenerator() {
 326         return findReportGenerator("html");
 327     }
 328 
 329     private ReportGenerator findReportGenerator(String name) {
 330         ReportGenerator rg = null;
 331         if (reportGeneratorSPIs != null) {
 332             for (ReportGeneratorSPI reportGeneratorSPI : reportGeneratorSPIs) {
 333                 rg = reportGeneratorSPI.getReportGenerator(name);
 334                 if (rg != null) {
 335                     return rg;
 336                 }
 337             }
 338         }
 339         return new DefaultReportGeneratorSPI().getReportGenerator(name); // can be null
 340     }
 341 
 342     protected DataRoot readDataRootFile(String filename, boolean readScales, String[] include, String[] exclude, String[] modif) throws FileFormatException {
 343         DataRoot file_image = null;
 344         ClassSignatureFilter acceptor = new ClassSignatureFilter(include, exclude, m_include, m_exclude, modif);
 345         file_image = Reader.readXML(filename, readScales, acceptor);
 346         return file_image;
 347     }
 348 
 349     /**
 350      * Legacy CMD line entry poiny (use 'java -jar jcov.jar Merger' from cmd
 351      * instead of 'java -cp jcov.jar com.sun.tdk.jcov.Merger')
 352      *
 353      * @param args
 354      */
 355     public static void main(String args[]) {
 356         RepGen tool = new RepGen();
 357         try {
 358             int res = tool.run(args);
 359             System.exit(res);
 360         } catch (Exception ex) {
 361             System.exit(1);
 362         }
 363     }
 364 
 365     protected String usageString() {
 366         return "java com.sun.tdk.jcov.RepGen [options] filename";
 367     }
 368 
 369     protected String exampleString() {
 370         return "java -cp jcov.jar com.sun.tdk.jcov.RepGen -include java.lang.* -format html -output out result.xml";
 371     }
 372 
 373     protected String getDescr() {
 374         return "generates text or HTML (or custom) reports";
 375     }
 376 
 377     private MemberFilter createCustomFilter(String spiName) {
 378         return FilterFactory.
 379                 getInstance(spiName).getMemberFilter();
 380 
 381     }
 382 
 383     private MemberFilter initCustomFilter(String filter, String sig) {
 384         MemberFilter customPlugin = null;
 385         MemberFilter sigFilter = null;
 386 
 387         if (filter != null) {
 388             customPlugin = createCustomFilter(filter);
 389         }
 390         if (customPlugin == null && sigFilter == null) {
 391             return null;
 392         } else if (customPlugin != null && sigFilter != null) {
 393             ConveyerFilter f = new ConveyerFilter();
 394             f.add(sigFilter);
 395             f.add(customPlugin);
 396             return f;
 397         } else {
 398             return sigFilter != null ? sigFilter : customPlugin;
 399         }
 400 
 401     }
 402 
 403     public void setReportGeneratorSPIs(ReportGeneratorSPI reportGeneratorSPI[]) {
 404         this.reportGeneratorSPIs = reportGeneratorSPI;
 405     }
 406 
 407     public ReportGeneratorSPI[] getReportGeneratorSPIs() {
 408         return reportGeneratorSPIs;
 409     }
 410 
 411     public String getFilter() {
 412         return filter;
 413     }
 414 
 415     public void setFilter(String filter) {
 416         this.filter = filter;
 417     }
 418 
 419     public String[] getFms() {
 420         return fms;
 421     }
 422 
 423     public void setFms(String[] fms) {
 424         this.fms = fms;
 425     }
 426 
 427     public String[] getInclude() {
 428         return include;
 429     }
 430 
 431     public void setInclude(String[] include) {
 432         this.include = include;
 433     }
 434 
 435     public boolean isIsPublicAPI() {
 436         return isPublicAPI;
 437     }
 438 
 439     public void setIsPublicAPI(boolean isPublicAPI) {
 440         this.isPublicAPI = isPublicAPI;
 441     }
 442 
 443     public boolean isNoAbstract() {
 444         return noAbstract;
 445     }
 446 
 447     public void setNoAbstract(boolean noAbstract) {
 448         this.noAbstract = noAbstract;
 449     }
 450 
 451     public boolean isSyntheticOn() {
 452         return syntheticOn;
 453     }
 454 
 455     public void setNoANC(boolean syntheticOn) {
 456         this.syntheticOn = syntheticOn;
 457     }
 458 
 459     public boolean isWithTestsInfo() {
 460         return withTestsInfo;
 461     }
 462 
 463     public void setWithTestsInfo(boolean withTestsInfo) {
 464         this.withTestsInfo = withTestsInfo;
 465     }
 466 
 467     public boolean isNoEnums() {
 468         return noEnums;
 469     }
 470 
 471     public void setNoEnums(boolean noEnums) {
 472         this.noEnums = noEnums;
 473     }
 474 
 475     public boolean isShowFields() {
 476         return showFields;
 477     }
 478 
 479     public void setShowFields(boolean showFields) {
 480 //        this.showFields = showFields;
 481     }
 482 
 483     public String[] getExclude() {
 484         return exclude;
 485     }
 486 
 487     public void setExclude(String[] exclude) {
 488         this.exclude = exclude;
 489     }
 490 
 491     public String getSrcRootPath() {
 492         return srcRootPath;
 493     }
 494 
 495     /**
 496      * Reset all properties to defaults. reportGeneratorSPI = null; include =
 497      * new String[] {".*"}; exclude = new String[] {""}; fms = null; filter =
 498      * null; generateShortFormat = false; noAbstract = false; isPublicAPI =
 499      * false; showMethods = true; showBlocks = true; showBranches = true;
 500      * showLines = true;
 501      */
 502     public void resetDefaults() {
 503         try {
 504             handleEnv_(defineHandler());
 505             reportGeneratorSPIs = null;
 506             //setFilters(new String[]{".*"}, new String[]{""}, null);
 507             setNoAbstract(false);
 508             setIsPublicAPI(false);
 509         } catch (EnvHandlingException ex) {
 510             // should not happen
 511         }
 512     }
 513 
 514     /**
 515      * Set all properties (except custorm report service provider)
 516      *
 517      * @param include patterns for including data. Set null for default value -
 518      * {".*"} (include all)
 519      * @param exclude patterns for excluding data. Set null for default value -
 520      * {""} (exclude nothing)
 521      * @param classModifiers modifiers that should have a class to be included
 522      * @param filter custom filter classname
 523      * @param generateShortFormat should generate short format
 524      * @param publicAPI should generate only public API (public and protected)
 525      * @param hideAbstract should hide abstract data
 526      * @param hideMethods should hide methods
 527      * @param hideBlocks should hide blocks
 528      * @param hideBranches should hide branches
 529      * @param hideLines should hide lines
 530      */
 531     public void configure(String[] include, String[] exclude, String[] classModifiers,
 532             String filter, boolean generateShortFormat, boolean publicAPI, boolean hideAbstract,
 533             boolean hideMethods, boolean hideBlocks, boolean hideBranches, boolean hideLines,
 534             boolean hideFields) {
 535         setFilters(include, exclude, classModifiers);
 536         setFilter(filter);
 537         this.isPublicAPI = publicAPI;
 538 //        this.showFields = !hideFields;
 539     }
 540 
 541     /**
 542      * Set filtering properties for the report
 543      *
 544      * @param include patterns for including data. Set null for default value -
 545      * {".*"} (include all)
 546      * @param exclude patterns for excluding data. Set null for default value -
 547      * {""} (exclude nothing)
 548      * @param classModifiers modifiers that should have a class to be included
 549      */
 550     public void setFilters(String[] include, String[] exclude, String[] classModifiers) {
 551         if (include == null) {
 552             include = new String[]{".*"};
 553         }
 554         this.include = include;
 555         if (exclude == null) {
 556             exclude = new String[]{""};
 557         }
 558         this.exclude = exclude;
 559         this.fms = classModifiers;
 560     }
 561 
 562     @Override
 563     protected int run() throws Exception {
 564         Result r;
 565         boolean srcZipped = false;
 566         if (srcRootPath != null) {
 567             File srcRootPathFile = new File(srcRootPath);
 568 
 569             if (srcRootPathFile.exists() && srcRootPathFile.isFile() && (srcRootPath.endsWith(".zip") || srcRootPath.endsWith(".jar"))) {
 570                 srcZipped = true;
 571                 srcRootPath = outputDir + File.separator + srcRootPathFile.getName().replace(".zip", "").replace(".jar", "");
 572                 Utils.unzipFolder(srcRootPathFile, srcRootPath);
 573             }
 574         }
 575 
 576         try {
 577 
 578             logger.log(Level.INFO, "-- Reading test list");
 579             if (filenames.length == 1) {
 580                 r = new Result(filenames[0], testlist);
 581             } else {
 582                 Merger merger = new Merger();
 583                 Result[] results = Merger.initResults(filenames, true);
 584                 Merger.Merge merge = new Merger.Merge(results, null);
 585                 merger.setAddMissing(true);
 586                 merger.setRead_scales(true);
 587                 merger.setDefaultReadingFilter(include, exclude, m_include, m_exclude, fms);
 588                 merger.merge(merge, outputDir, true);
 589 
 590                 ReportGenerator rg;
 591                 if (name != null) {
 592                     rg = findReportGenerator(name);
 593                 } else {
 594                     rg = getDefaultReportGenerator();
 595                 }
 596                 if (rg == null) {
 597                     throw new Exception("Specified ReportGenerator name (" + name + ") was not found");
 598                 }
 599                 rg.init(outputDir);
 600                 String[] tl = testlist != null ? Utils.readLines(testlist) : merge.getResultTestList();
 601                 SmartTestService sts = new SmartTestService(tl);
 602                 ReportGenerator.Options options = new ReportGenerator.Options(srcRootPath, sts, null, true, true,
 603                         mainReportTitle, overviewListTitle, entitiesTitle);
 604                 try {
 605                     DataRoot mergedResult = merge.getResult();
 606                     if (!syntheticOn) {
 607                         mergedResult.applyFilter(new ANC_FILTER());
 608                     }
 609 
 610                     if (dataProcessorSPIs != null) {
 611                         for (DataProcessorSPI spi : dataProcessorSPIs) {
 612                             logger.log(Level.INFO, "-- Applying data processor {0}", spi.getClass());
 613                             mergedResult = spi.getDataProcessor().process(mergedResult);
 614                         }
 615                     }
 616 
 617                     ProductCoverage coverage = new ProductCoverage(mergedResult, options.getSrcRootPaths(), null, isPublicAPI, noAbstract, ancfiltersClasses);
 618                     rg.generateReport(coverage, options);
 619 
 620                     if (srcZipped) {
 621                         Utils.deleteDirectory(new File(srcRootPath));
 622                     }
 623 
 624                 } catch (Throwable ex) {
 625                     if (ex.getMessage() != null) {
 626                         throw new Exception("ReportGenerator produced exception " + ex.getMessage(), ex);
 627                     } else {
 628                         throw new Exception("ReportGenerator produced exception " + ex, ex);
 629                     }
 630                 }
 631 
 632                 return 0;
 633             }
 634         } catch (IOException ex) {
 635             logger.log(Level.SEVERE, "Error while reading testlist", ex);
 636             return 1;
 637         }
 638 
 639         if (classesPath != null) {
 640             try {
 641                 logger.log(Level.INFO, "-- Creating javap report");
 642                 new JavapRepGen(include, exclude).run(filenames[0], classesPath, outputDir);
 643                 return 0;
 644             } catch (Exception ex) {
 645                 logger.log(Level.SEVERE, "Error while creating javap report", ex);
 646                 return 1;
 647             }
 648         }
 649 
 650         try {
 651             generateReport(name, outputDir, r, srcRootPath);
 652 
 653             if (srcZipped) {
 654                 Utils.deleteDirectory(new File(srcRootPath));
 655             }
 656 
 657             return 0;
 658         } catch (FileFormatException e) {
 659 //            logger.log(Level.SEVERE, "malformed jcov file \"{0}", filename);
 660             logger.log(Level.SEVERE, e.getMessage(), Arrays.toString(filenames));
 661         } catch (ProcessingException ex) {
 662         } catch (Exception ex) {
 663             logger.log(Level.SEVERE, ex.getMessage(), ex);
 664         }
 665         return SUCCESS_EXIT_CODE;
 666     }
 667 
 668     @Override
 669     protected EnvHandler defineHandler() {
 670         EnvHandler envHandler = new EnvHandler(new OptionDescr[]{
 671                     DSC_FMT,
 672                     DSC_OUTPUT,
 673                     //            DSC_STDOUT,
 674                     com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE,
 675                     com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE,
 676                     com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE_LIST,
 677                     com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE_LIST,
 678                     com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_MINCLUDE_LIST,
 679                     com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_MEXCLUDE_LIST,
 680                     com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_MINCLUDE,
 681                     com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_MEXCLUDE,
 682                     com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_FM,
 683                     com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_FM_LIST,
 684                     DSC_NO_ABSTRACT,
 685                     DSC_SYNTHETIC_ON,
 686                     DSC_PUBLIC_API,
 687                     DSC_SRC_ROOT,
 688                     DSC_VERBOSE,
 689                     DSC_FILTER_PLUGIN,
 690                     DSC_ANC_FILTER_PLUGINS,
 691                     DSC_ANC_DEFAULT_FILTERS,
 692                     DSC_TEST_LIST,
 693                     DSC_ANONYM,
 694                     DSC_JAVAP,
 695                     DSC_TESTS_INFO,
 696                     DSC_REPORT_TITLE_MAIN,
 697                     DSC_REPORT_TITLE_OVERVIEW,
 698                     DSC_REPORT_TITLE_ENTITIES,}, this);
 699         SPIDescr spiDescr = new SPIDescr(CUSTOM_REPORT_GENERATOR_SPI, ReportGeneratorSPI.class);
 700         spiDescr.setDefaultSPI(new DefaultReportGeneratorSPI());
 701         envHandler.registerSPI(spiDescr);
 702 
 703         spiDescr = new SPIDescr(DATA_PROCESSOR_SPI, DataProcessorSPI.class);
 704         spiDescr.addPreset("none", new StubSpi());
 705         spiDescr.setDefaultSPI(new DefaultDataProcessorSPI());
 706         envHandler.registerSPI(spiDescr);
 707 
 708         return envHandler;
 709     }
 710 
 711     private int handleEnv_(EnvHandler opts) throws EnvHandlingException {
 712         if (opts.isSet(DSC_VERBOSE)) {
 713 //            LoggingFormatter.printStackTrace = true; // by default logger doesn't print stacktrace
 714             logger.setLevel(Level.INFO);
 715         } else {
 716             logger.setLevel(Level.SEVERE);
 717         }
 718 
 719         name = opts.getValue(DSC_FMT);
 720         outputDir = opts.getValue(DSC_OUTPUT);
 721         // no check for output
 722 
 723         filter = opts.getValue(DSC_FILTER_PLUGIN);
 724         ancfilters = opts.getValues(DSC_ANC_FILTER_PLUGINS);
 725         ancdeffilters = opts.getValues(DSC_ANC_DEFAULT_FILTERS);
 726         noAbstract = opts.isSet(DSC_NO_ABSTRACT);
 727         isPublicAPI = opts.isSet(DSC_PUBLIC_API);
 728         anonym = opts.isSet(DSC_ANONYM);
 729         syntheticOn = opts.isSet(DSC_SYNTHETIC_ON);
 730 
 731         include = InstrumentationOptions.handleInclude(opts);
 732         exclude = InstrumentationOptions.handleExclude(opts);
 733         fms = InstrumentationOptions.handleFM(opts);
 734 
 735         m_include = InstrumentationOptions.handleMInclude(opts);
 736         m_exclude = InstrumentationOptions.handleMExclude(opts);
 737 
 738         testlist = opts.getValue(DSC_TEST_LIST);
 739         Utils.checkFileCanBeNull(testlist, "testlist filename", Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_CANREAD, Utils.CheckOptions.FILE_ISFILE);
 740 
 741         withTestsInfo = opts.isSet(DSC_TESTS_INFO);
 742 
 743         srcRootPath = null;
 744         if (opts.isSet(DSC_SRC_ROOT)) {
 745             srcRootPath = opts.getValue(DSC_SRC_ROOT);
 746         }
 747 
 748         if (opts.isSet(DSC_REPORT_TITLE_MAIN)){
 749             mainReportTitle = opts.getValue(DSC_REPORT_TITLE_MAIN);
 750         }
 751         if (opts.isSet(DSC_REPORT_TITLE_OVERVIEW)){
 752             overviewListTitle = opts.getValue(DSC_REPORT_TITLE_OVERVIEW);
 753         }
 754         if (opts.isSet(DSC_REPORT_TITLE_ENTITIES)){
 755             entitiesTitle = opts.getValue(DSC_REPORT_TITLE_ENTITIES);
 756         }
 757 
 758         ArrayList<ReportGeneratorSPI> reportGenerators = opts.getSPIs(ReportGeneratorSPI.class);
 759         if (reportGenerators != null) {
 760             reportGeneratorSPIs = reportGenerators.toArray(new ReportGeneratorSPI[reportGenerators.size()]);
 761         }
 762         ArrayList<DataProcessorSPI> dataProcessors = opts.getSPIs(DataProcessorSPI.class);
 763         if (dataProcessors != null) {
 764             dataProcessorSPIs = dataProcessors.toArray(new DataProcessorSPI[dataProcessors.size()]);
 765         }
 766 
 767         classesPath = opts.getValue(DSC_JAVAP);
 768 
 769         return SUCCESS_EXIT_CODE;
 770     }
 771 
 772     @Override
 773     protected int handleEnv(EnvHandler opts) throws EnvHandlingException {
 774         String[] srcs = opts.getTail();
 775         if (srcs == null) {
 776             throw new EnvHandlingException("no input files specified");
 777         }
 778 
 779         filenames = srcs;
 780         Utils.checkFileNotNull(filenames[0], "JCov datafile", Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_CANREAD, Utils.CheckOptions.FILE_ISFILE);
 781         /*if (srcs.length > 1) {
 782          logger.log(Level.WARNING,"only \"{0}\" will be processed, the rest of the files will be ignored\n" +
 783          "\tto generate report for all files, merge them into one using the merger utility", filename);
 784          }*/
 785 
 786         return handleEnv_(opts);
 787     }
 788 
 789     public static class ANC_FILTER implements MemberFilter {
 790 
 791         @Override
 792         public boolean accept(DataClass clz) {
 793             return true;
 794         }
 795 
 796         @Override
 797         public boolean accept(DataClass clz, DataMethod m) {
 798 
 799             boolean ancMethod;
 800 
 801             //Synthetic method (and Bridge method)
 802             ancMethod = ((m.getAccess() & Opcodes.ACC_SYNTHETIC) != 0);
 803 
 804             //Enum method
 805             ancMethod = ancMethod
 806                     || (clz.getSuperName().equals("java/lang/Enum") && (m.getName().equals("valueOf") || m.getName().equals("values")));
 807 
 808             return !ancMethod || m.getName().startsWith("lambda$");
 809 
 810         }
 811 
 812         @Override
 813         public boolean accept(DataClass clz, DataField f) {
 814             return true;
 815         }
 816     }
 817     final static OptionDescr DSC_FMT =
 818             new OptionDescr("format", new String[]{"fmt"},
 819             "Report generation output.", VAL_SINGLE,
 820             "Specifies the format of the report.\n"
 821             + "Use \"text\" for generate text report and \"html\" for generate HTML report\n"
 822             + "Text report in one file which contains method/block/branch coverage information.\n"
 823             + "HTML report contains coverage information with marked-up sources.\n\n"
 824             + "Custom reports can be specified with ReportGeneratorSPI interface.", "html");
 825     /**
 826      *
 827      */
 828     public final static OptionDescr DSC_OUTPUT =
 829             new OptionDescr("repgen.output", new String[]{"output", "o"}, "", OptionDescr.VAL_SINGLE,
 830             "Output directory for generating text and HTML reports.", "report");
 831     /**
 832      *
 833      */
 834     public final static OptionDescr DSC_TEST_LIST =
 835             new OptionDescr("tests", "Test list", OptionDescr.VAL_SINGLE,
 836             "Specify the path to the file containing test list. File should contain a list of tests\n"
 837             + "with one name per line.");
 838     final static OptionDescr DSC_FILTER_PLUGIN =
 839             new OptionDescr("filter", "", OptionDescr.VAL_SINGLE,
 840             "Custom filtering plugin class");
 841 
 842     final static OptionDescr DSC_ANC_FILTER_PLUGINS =
 843             new OptionDescr("ancfilter", new String[]{"ancf"}, "Custom anc filtering plugin classes", OptionDescr.VAL_MULTI,
 844                     "");
 845 
 846     final static OptionDescr DSC_ANC_DEFAULT_FILTERS =
 847             new OptionDescr("ancdeffilters", new String[]{"ancdf"}, "Default ANC filters names to use in report", OptionDescr.VAL_MULTI,
 848                     "");
 849 
 850     /**
 851      *
 852      */
 853     public final static OptionDescr DSC_SRC_ROOT =
 854             new OptionDescr("sourcepath", new String[]{"source", "src"}, "The source files.", OptionDescr.VAL_SINGLE, "");
 855     final static OptionDescr DSC_VERBOSE =
 856             new OptionDescr("verbose", "Verbosity.", "Enable verbose mode.");
 857     /**
 858      *
 859      */
 860     public final static OptionDescr DSC_NO_ABSTRACT =
 861             new OptionDescr("noabstract", "Additional filtering", "Do not count abstract methods");
 862     public final static OptionDescr DSC_SYNTHETIC_ON =
 863             new OptionDescr("syntheticon", "Additional filtering", "Count coverage for synthetic methods");
 864     /**
 865      *
 866      */
 867     public final static OptionDescr DSC_PUBLIC_API =
 868             new OptionDescr("publicapi", "", "Count only public and protected members");
 869     public final static OptionDescr DSC_ANONYM =
 870             new OptionDescr("anonym", "", "include methods from anonymous classes into the report");
 871     public final static OptionDescr DSC_JAVAP =
 872             new OptionDescr("javap", new String[]{"javap"}, "Path to the class files of the product to use javap", OptionDescr.VAL_SINGLE, "");
 873     public final static OptionDescr DSC_TESTS_INFO =
 874             new OptionDescr("testsinfo", "Additional information about for specified tests' list", "Show covererage for all tests in test list");
 875 
 876     public final static OptionDescr DSC_REPORT_TITLE_MAIN =
 877             new OptionDescr("mainReportTitle", new String[]{"mainReportTitle", "mrtitle"}, "The main report title", OptionDescr.VAL_SINGLE, "");
 878     public final static OptionDescr DSC_REPORT_TITLE_OVERVIEW =
 879             new OptionDescr("overviewReportTitle", new String[]{"overviewReportTitle", "ortitle"}, "The overview list report title", OptionDescr.VAL_SINGLE, "");
 880     public final static OptionDescr DSC_REPORT_TITLE_ENTITIES =
 881             new OptionDescr("entitiesReportTitle", new String[]{"entitiesReportTitle", "ertitle"}, "Entities report title (for modules, packages, subpackages)", OptionDescr.VAL_SINGLE, "");
 882 }