com.atlassian.clover.CloverInstr.java Source code

Java tutorial

Introduction

Here is the source code for com.atlassian.clover.CloverInstr.java

Source

package com.atlassian.clover;

import clover.org.apache.commons.lang3.StringUtils;
import com.atlassian.clover.api.CloverException;
import com.atlassian.clover.cfg.instr.InstrumentationConfig;
import com.atlassian.clover.cfg.instr.MethodContextDef;
import com.atlassian.clover.cfg.instr.java.JavaInstrumentationConfig;
import com.atlassian.clover.cfg.instr.java.LambdaInstrumentation;
import com.atlassian.clover.instr.java.Instrumenter;
import com.atlassian.clover.context.ContextStore;
import com.atlassian.clover.remote.DistributedConfig;
import com.atlassian.clover.util.FileUtils;
import com.atlassian.clover.util.SourceScanner;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Locale;

import static clover.com.google.common.collect.Lists.newArrayList;

/**
 *
 */
public class CloverInstr {

    private JavaInstrumentationConfig cfg;

    private File inDir;
    private File outDir;
    private List<String> srcFiles = newArrayList();
    private Logger log = Logger.getInstance();

    private CloverInstr() {

    }

    public static void main(String[] args) {
        System.exit(mainImpl(args));
    }

    /**
     * sames as "main", but returns 0 on success
     */
    public static int mainImpl(String[] args) {

        CloverStartup.loadLicense(Logger.getInstance());

        CloverInstr instr = new CloverInstr();

        if (!instr.processArgs(args)) {
            return 1;
        }

        return instr.execute();
    }

    public int execute() {
        log = Logger.getInstance();
        final Instrumenter instr = new Instrumenter(log, cfg);

        try {
            ContextStore.saveCustomContexts(cfg);
            instr.startInstrumentation();
        } catch (CloverException e) {
            log.error("Could not initialise Clover: " + e.getMessage());
            return 1;
        }

        try {
            // scan the source dir if supplied
            if (inDir != null) {
                srcFiles.addAll(scanSrcDir(inDir));
            }
            for (String srcFile : srcFiles) {
                instr.instrument(new File(srcFile), outDir, cfg.getEncoding());
            }

            instr.endInstrumentation();
        } catch (Exception e) {
            log.error("Instrumentation error", e);
            return 1;
        }
        return 0;
    }

    private List<String> scanSrcDir(final File dir) throws IOException {
        final List<String> files = newArrayList();
        SourceScanner scanner = new SourceScanner(dir, ".*\\.java");
        scanner.visit(new SourceScanner.Visitor() {

            @Override
            public void common(String path) throws IOException {
                files.add(new File(dir, FileUtils.getNormalizedPath(path)).getAbsolutePath());
            }

            @Override
            public void onlyInSrc(String path) throws IOException {
                files.add(new File(dir, FileUtils.getNormalizedPath(path)).getAbsolutePath());
            }

            @Override
            public void onlyInDest(String path) {
                // no-op
            }
        });

        return files;
    }

    private static void usage(String msg) {
        System.err.println();
        if (msg != null) {
            System.err.println("  *** ERROR: " + msg);
        }
        System.err.println();
        System.err.println("  USAGE: " + CloverInstr.class.getName() + " [OPTIONS] PARAMS [FILES...]");
        System.err.println();
        System.err.println("  PARAMS:");
        System.err.println(
                "    -s, --srcdir <dir>\t\t Directory containing source files to be instrumented. If omitted");
        System.err.println("\t\t\t\t individual source files should be specified on the command line.");
        System.err.println();
        System.err.println(
                "    -d, --destdir <dir>\t\t Directory where Clover should place the instrumented sources.");
        System.err.println();
        System.err.println();
        System.err.println("  OPTIONS:");
        System.err.println("    -i, --initstring <file>\t Clover initstring. This is the path to the dbfile that");
        System.err.println("\t\t\t\t will be used to construct/update to store coverage data.");
        System.err.println();
        System.err.println(
                "    -dc, --distributedCoverage <string>\t Configuration for recording distributed pre-test coverage. ");
        System.err.println("\t\t\t\t Valid keys and default values are: ON | OFF | " + new DistributedConfig());
        System.err.println();
        System.err.println("    -r, --relative\t If specified, the initstring is treated as a relative path, ");
        System.err.println("\t\t\t\t rather than being converted to an absolute path.");
        System.err.println("\t\t\t\t This is useful for distributed testing environments.");
        System.err.println();
        System.err.println(
                "    -p, --flushpolicy <policy>\t Set the flushpolicy Clover will use during coverage recording.");
        System.err.println(
                "\t\t\t\t legal values are \"directed\",\"interval\",\"threaded\". Default is \"directed\". If");
        System.err.println(
                "\t\t\t\t either \"interval\" or \"threaded\" policies is used, the flushinterval must also be set");
        System.err.println("\t\t\t\t using the -f option.");
        System.err.println(
                "    -f, --flushinterval <int>\t Set the interval between flushes (in millisecs). Only applies to");
        System.err.println("\t\t\t\t \"interval\" or \"threaded\" flush policies.");
        System.err
                .println("    -e, --encoding <encoding>\t Set the File encoding to use when reading source files.");
        System.err.println();
        System.err.println(
                "    --instrumentation <string>\t Set the instrumentation strategy. Valid values are \"field\" and");
        System.err.println("\t\t\t\t \"class\". Default is \"class\".");
        System.err.println();
        System.err.println(
                "    --instrlevel <string>\t Set the instrumentation level. Valid values are \"statement\" and");
        System.err.println("\t\t\t\t \"method\". Default is \"statement\".");
        System.err.println();
        System.err.println(
                "    --instrlambda <string>\t Set whether lambda functions shall be instrumented. Valid values are: "
                        + StringUtils.join(LambdaInstrumentation.values(), ", ").toLowerCase(Locale.ENGLISH) + ".");
        System.err.println("\t\t\t\t Default is " + LambdaInstrumentation.NONE + ".");
        System.err.println();
        System.err.println("    --source <level>\t\t Set the language level for Clover to use when parsing files.");
        System.err.println();
        System.err.println(
                "    --recordTestResults <true|false> \t If set to false, Clover will not record test results.");
        System.err.println();
        System.err.println(
                "    --dontFullyQualifyJavaLang \t\t If set, then java.lang will not be used in instrumented source.");
        System.err.println();
        System.err.println(
                "    -mc --methodContext <name>=<regexp> \t\t Defines a single custom method context. May be supplied more than once. (\\ may be needed to prevent shell expansion)");
        System.err.println();
        System.err.println(
                "    -sc --statementContext <name>=<regexp>\t\t Defines a single custom statement context. May be supplied more than once.(\\ may be needed to prevent shell expansion)");
        System.err.println();
        System.err.println("    -v, --verbose\t\t Enable verbose logging.");
    }

    private boolean processArgs(String[] args) {

        cfg = new JavaInstrumentationConfig();
        try {
            int i = 0;

            while (i < args.length) {
                if (args[i].equals("-i") || args[i].equals("--initstring")) {
                    i++;
                    cfg.setInitstring(args[i]);
                } else if (args[i].equals("-r") || args[i].equals("--relative")) {
                    cfg.setRelative(true);
                } else if (args[i].equals("-s") || args[i].equals("--srcdir")) {
                    i++;
                    inDir = (new File(args[i])).getAbsoluteFile();
                } else if (args[i].equals("-d") || args[i].equals("--destdir")) {
                    i++;
                    outDir = (new File(args[i])).getAbsoluteFile();
                } else if (args[i].equals("-e") || args[i].equals("--encoding")) {
                    i++;
                    cfg.setEncoding(args[i]);
                } else if (args[i].equals("--recordTestResults")) {
                    i++;
                    cfg.setRecordTestResults(Boolean.valueOf(args[i]).booleanValue());
                } else if (args[i].equals("-dc") || args[i].equals("--distributedCoverage")) {
                    i++;
                    cfg.setDistributedConfig(new DistributedConfig(args[i]));
                } else if (args[i].equalsIgnoreCase("--dontFullyQualifyJavaLang")) {
                    cfg.setFullyQualifyJavaLang(false);
                } else if (args[i].equals("-p") || args[i].equals("--flushpolicy")) {
                    i++;
                    String policy = args[i];
                    try {
                        cfg.setFlushPolicyFromString(policy);
                    } catch (CloverException e) {
                        usage(e.getMessage());
                        return false;
                    }
                } else if (args[i].equals("-f") || args[i].equals("--flushinterval")) {
                    i++;
                    try {
                        cfg.setFlushInterval(Integer.parseInt(args[i]));
                        if (cfg.getFlushInterval() <= 0) {
                            throw new NumberFormatException();
                        }
                    } catch (NumberFormatException e) {
                        usage("expecting a positive integer value for flush interval");
                        return false;
                    }
                } else if (args[i].equals("-u") || args[i].equals("--useclass")) {
                    i++;
                    log.warn("the useclass parameter has been deprecated and will be ignored.");
                } else if (args[i].equals("--source")) {
                    i++;
                    cfg.setSourceLevel(args[i]);
                } else if (args[i].equals("--instrumentation")) {
                    i++;
                    String instr = args[i];
                    cfg.setInstrStrategy(instr);
                } else if (args[i].equals("--instrlevel")) {
                    i++;
                    String instr = args[i];
                    cfg.setInstrLevelStrategy(instr);
                } else if (args[i].equals("--instrlambda")) {
                    try {
                        i++;
                        cfg.setInstrumentLambda(LambdaInstrumentation.valueOf(args[i].toUpperCase(Locale.ENGLISH)));
                    } catch (IllegalArgumentException ex) {
                        usage("Invalid value: " + args[i] + ". " + ex.getMessage());
                        return false;
                    }
                } else if (args[i].equals("-v") || args[i].equals("--verbose")) {
                    Logger.setVerbose(true);
                } else if (args[i].equals("-mc") || args[i].equals("--methodContext")) {
                    // expected in the format: name=value, where value may have one or more '='
                    i++;
                    try {
                        cfg.addMethodContext(parseContextDef(args[i]));
                    } catch (CloverException e) {
                        usage("Could not parse custom method context definition: " + args[i] + ". "
                                + e.getMessage());
                        return false;
                    }
                } else if (args[i].equals("-sc") || args[i].equals("--statementContext")) {
                    // expected in the format: name=value, where value may have one or more '='
                    i++;
                    try {
                        cfg.addStatementContext(parseContextDef(args[i]));
                    } catch (CloverException e) {
                        usage("Could not parse custom statement context definition: " + args[i] + ". "
                                + e.getMessage());
                        return false;
                    }
                } else if (args[i].endsWith(".java")) {
                    srcFiles.add(args[i]);
                }
                i++;
            }

            if (cfg.getInitString() == null) {
                try {
                    cfg.createDefaultInitStringDir();
                } catch (CloverException e) {
                    usage("No --initstring value supplied, and default location could not be created: "
                            + e.getMessage());
                    return false;
                }
            }

            if (inDir == null && srcFiles.size() == 0) {
                usage("No source files specified");
                return false;
            } else if (outDir == null) {
                usage("No Destination dir specified");
                return false;
            } else if ((cfg.getFlushPolicy() == InstrumentationConfig.INTERVAL_FLUSHING
                    || cfg.getFlushPolicy() == InstrumentationConfig.THREADED_FLUSHING)
                    && cfg.getFlushInterval() == 0) {
                usage("When using either \"interval\" or \"threaded\" flushpolicy, a flushinterval must be specified.");
                return false;
            } else {
                if (inDir != null) {
                    if (inDir.equals(outDir)) {
                        usage("Srcdir and destdir cannot be the same.");
                        return false;
                    }

                    // check to see that indir is not a parent of outdir
                    File outParent = outDir.getParentFile();
                    while (outParent != null) {
                        if (outParent.equals(inDir)) {
                            usage("Cannot specify a destdir that is a nested dir of the srcdir.");
                            return false;
                        }
                        outParent = outParent.getParentFile();
                    }

                    if (cfg.getFlushPolicy() == InstrumentationConfig.DIRECTED_FLUSHING
                            && cfg.getFlushInterval() != 0) {
                        log.warn(
                                "ignoring flushinterval since flushpolicy is directed. To specify interval flushing, use -p interval.");
                    }
                }
                return true;
            }

        } catch (ArrayIndexOutOfBoundsException e) {
            usage("Missing a parameter.");
        }
        return false;
    }

    private MethodContextDef parseContextDef(String line) throws CloverException {
        final int index = line.indexOf('=');
        if (index <= 0 || (index + 1 == line.length())) {
            throw new CloverException("Custom context definitions must be of the form: 'name=regexp'");
        }
        final MethodContextDef contextDef = new MethodContextDef();
        contextDef.setName(line.substring(0, index));
        contextDef.setRegexp(line.substring(index + 1, line.length()));
        return contextDef;
    }
}