com.madvay.tools.android.perf.apat.CommandLine.java Source code

Java tutorial

Introduction

Here is the source code for com.madvay.tools.android.perf.apat.CommandLine.java

Source

/*
 * Copyright (c) 2015 by Advay Mengle.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.madvay.tools.android.perf.apat;

import com.madvay.tools.android.perf.common.FilterSpec;
import com.madvay.tools.android.perf.common.StePredicates;
import com.madvay.tools.android.perf.common.TraceTransformers;

import com.google.common.base.*;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

/**
 *
 */
public class CommandLine {

    public final String command;

    public final List<String> args;

    public final Multimap<String, String> flags;

    private static final Splitter KV_SPLIT = Splitter.on('=').limit(2);
    private static final Splitter SPEC_SPLIT = Splitter.on(':').limit(2);
    private static final Splitter CONJ_SPLIT = Splitter.on(',');
    private static final Splitter LIST_SPLIT = Splitter.on(',');

    public CommandLine(String[] argv) {
        if (argv.length < 1) {
            throw new IllegalArgumentException("1st argument must be name of a command.");
        }
        command = argv[0];

        args = new ArrayList<>();
        flags = LinkedListMultimap.create();
        List<String> allArgs = Lists.newArrayList(argv);
        allArgs.remove(0);
        parseArgs(allArgs);
    }

    private void parseArgs(Iterable<String> allArgs) {
        for (String arg : allArgs) {
            parseArg(arg);
        }
    }

    private boolean handleExpandedArg(String arg, List<String> kv) {
        List<String> args = null;
        switch (arg) {
        case "autoboxing":
            args = ImmutableList
                    .of("--allocated=re:" + "java.lang.(Integer|Long|Double|Float|Byte|Short|Character)");
            break;
        default:
            return false;
        }
        parseArgs(args);
        return true;
    }

    private void parseArg(String arg) {
        if (arg.startsWith("--")) {
            arg = arg.substring(2);
            List<String> kv = KV_SPLIT.splitToList(arg);
            if (handleExpandedArg(arg, kv)) {
                // All done!
            } else if (kv.get(0).equals("config")) {
                handleConfigFile(kv.get(1));
            } else {
                flags.put(kv.get(0), kv.get(1));
            }
        } else {
            args.add(arg);
        }
    }

    private void handleConfigFile(String fname) {
        List<String> internalArgs;
        try {
            internalArgs = Files.readAllLines(Paths.get(fname), Charsets.UTF_8);
        } catch (IOException err) {
            throw new IllegalArgumentException("Cannot load config file: " + fname, err);
        }
        for (String arg : internalArgs) {
            arg = arg.trim();
            // Skip comments
            if (arg.startsWith("#") || arg.isEmpty()) {
                continue;
            }
            parseArg(arg);
        }
    }

    public String getUnaryFlagWithDefault(String name, String def) {
        return Iterables.getLast(flags.get(name), def);
    }

    public String getJoinedListFlagWithDefault(String name, String def) {
        List<String> multiFlag = getMultiFlag(name);
        if (multiFlag.isEmpty()) {
            return def;
        }
        return Joiner.on(',').join(multiFlag);
    }

    public List<String> getMultiFlag(String name) {
        return Lists.newArrayList(flags.get(name));
    }

    public List<String> getMultiFlagWithInternalLists(String name) {
        return Lists.newArrayList(
                Iterables.concat(Iterables.transform(flags.get(name), new Function<String, List<String>>() {
                    @Override
                    public List<String> apply(String input) {
                        return LIST_SPLIT.splitToList(input);
                    }
                })));
    }

    public List<FilterSpec> getFilterSpecsFlag(final String column) {
        List<String> flagStr = getMultiFlag(column);
        return Lists.transform(flagStr, new Function<String, FilterSpec>() {
            @Override
            public FilterSpec apply(String input) {
                if (input == null || input.isEmpty()) {
                    return null;
                }
                List<String> spl = SPEC_SPLIT.splitToList(input);
                if (spl.size() == 2) {
                    FilterSpec.FilterType t = filterTypeByPrefix(spl.get(0));
                    return new FilterSpec(column, t, spl.get(1));
                } else if (spl.size() == 1) {
                    // Assume equals.
                    return new FilterSpec(column, FilterSpec.FilterType.EQUALS, spl.get(0));
                } else {
                    throw new IllegalArgumentException("Bad filter spec: " + input);
                }
            }
        });
    }

    public List<TraceTransformers.TT> getTraceTransformsFlag(final String name) {
        List<String> flagStr = getMultiFlag(name);
        return Lists.transform(flagStr, new Function<String, TraceTransformers.TT>() {
            @Override
            public TraceTransformers.TT apply(String input) {
                if (input == null || input.isEmpty()) {
                    return null;
                }
                return parseTraceTransform(input);
            }
        });
    }

    private static Predicate<StackTraceElement> parseSteP(String s) {
        return Predicates.and(
                Lists.transform(CONJ_SPLIT.splitToList(s), new Function<String, Predicate<StackTraceElement>>() {
                    @Override
                    public Predicate<StackTraceElement> apply(String input) {
                        return parseStePOperator(input);
                    }
                }));
    }

    private static Predicate<StackTraceElement> parseStePOperator(String s) {
        List<String> spl = SPEC_SPLIT.splitToList(s);
        String operator = spl.get(0);
        if (spl.size() == 1) {
            return StePredicates.contains(operator);
        }
        String arg = spl.get(1);
        switch (operator) {
        case "underPackage":
            return StePredicates.underPackage(arg);
        case "inPackage":
            return StePredicates.inPackage(arg);
        case "class":
            return StePredicates.classContains(arg);
        case "classEq":
            return StePredicates.classEq(arg);
        case "classRe":
            return StePredicates.classRe(arg);
        case "method":
            return StePredicates.methodContains(arg);
        case "methodEq":
            return StePredicates.methodEq(arg);
        case "methodRe":
            return StePredicates.methodRe(arg);
        case "site":
            return StePredicates.siteContains(arg);
        case "siteEq":
            return StePredicates.siteEq(arg);
        case "siteRe":
            return StePredicates.siteRe(arg);
        case "contains":
            return StePredicates.contains(arg);
        default:
            throw new IllegalArgumentException("Unknown element spec: " + s);
        }
    }

    private static TraceTransformers.TT parseTraceTransform(String s) {
        List<String> spl = SPEC_SPLIT.splitToList(s);
        switch (spl.get(0)) {
        case "pruneRecursion":
            return TraceTransformers.pruneRecursion();
        case "prune":
            return TraceTransformers.prune(parseSteP(spl.get(1)));
        case "keep":
            return TraceTransformers.keep(parseSteP(spl.get(1)));
        case "pruneAbove":
            return TraceTransformers.pruneAbove(parseSteP(spl.get(1)));
        case "pruneBelow":
            return TraceTransformers.pruneBelow(parseSteP(spl.get(1)));
        case "keepAbove":
            return TraceTransformers.keepAbove(parseSteP(spl.get(1)));
        case "keepBelow":
            return TraceTransformers.keepBelow(parseSteP(spl.get(1)));
        default:
            throw new IllegalArgumentException("Unknown trace transform: " + s);
        }
    }

    private static FilterSpec.FilterType filterTypeByPrefix(String pr) {
        switch (pr) {
        case "eq":
            return FilterSpec.FilterType.EQUALS;
        case "ne":
        case "neq":
            return FilterSpec.FilterType.NOT_EQUALS;
        case "lt":
        case "l":
            return FilterSpec.FilterType.LESS;
        case "le":
        case "leq":
            return FilterSpec.FilterType.LEQ;
        case "gt":
        case "g":
            return FilterSpec.FilterType.GREATER;
        case "ge":
        case "geq":
            return FilterSpec.FilterType.GEQ;
        case "re":
            return FilterSpec.FilterType.RE_MATCH;
        case "nre":
            return FilterSpec.FilterType.NOT_RE_MATCH;
        case "contains":
        case "ss":
            return FilterSpec.FilterType.CONTAINS;
        case "notcontains":
        case "nss":
            return FilterSpec.FilterType.NOT_CONTAINS;
        default:
            throw new IllegalArgumentException("Bad filter type: " + pr);
        }
    }
}