com.google.caliper.runner.config.CaliperConfig.java Source code

Java tutorial

Introduction

Here is the source code for com.google.caliper.runner.config.CaliperConfig.java

Source

/*
 * Copyright (C) 2012 Google Inc.
 *
 * 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.google.caliper.runner.config;

import static com.google.caliper.util.Util.subgroupMap;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import com.google.caliper.api.ResultProcessor;
import com.google.caliper.runner.options.CaliperOptions;
import com.google.caliper.util.Util;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;

/**
 * Represents caliper configuration. By default, {@code ~/.caliper/config.properties} and {@code
 * global-config.properties}.
 */
public final class CaliperConfig {
    @VisibleForTesting
    final ImmutableMap<String, String> properties;
    private final ImmutableMap<Class<? extends ResultProcessor>, ResultProcessorConfig> resultProcessorConfigs;

    @VisibleForTesting
    public CaliperConfig(ImmutableMap<String, String> properties) throws InvalidConfigurationException {
        this.properties = checkNotNull(properties);
        this.resultProcessorConfigs = findResultProcessorConfigs(subgroupMap(properties, "results"));
    }

    private static final Pattern CLASS_PROPERTY_PATTERN = Pattern.compile("(\\w+)\\.class");

    private static <T> ImmutableBiMap<String, Class<? extends T>> mapGroupNamesToClasses(
            ImmutableMap<String, String> groupProperties, Class<T> type) throws InvalidConfigurationException {
        BiMap<String, Class<? extends T>> namesToClasses = HashBiMap.create();
        for (Entry<String, String> entry : groupProperties.entrySet()) {
            Matcher matcher = CLASS_PROPERTY_PATTERN.matcher(entry.getKey());
            if (matcher.matches() && !entry.getValue().isEmpty()) {
                try {
                    Class<?> someClass = Util.loadClass(entry.getValue());
                    checkState(type.isAssignableFrom(someClass));
                    @SuppressWarnings("unchecked")
                    Class<? extends T> verifiedClass = (Class<? extends T>) someClass;
                    namesToClasses.put(matcher.group(1), verifiedClass);
                } catch (ClassNotFoundException e) {
                    throw new InvalidConfigurationException(
                            "Cannot find result processor class: " + entry.getValue());
                }
            }
        }
        return ImmutableBiMap.copyOf(namesToClasses);
    }

    private static ImmutableMap<Class<? extends ResultProcessor>, ResultProcessorConfig> findResultProcessorConfigs(
            ImmutableMap<String, String> resultsProperties) throws InvalidConfigurationException {
        ImmutableBiMap<String, Class<? extends ResultProcessor>> processorToClass = mapGroupNamesToClasses(
                resultsProperties, ResultProcessor.class);
        ImmutableMap.Builder<Class<? extends ResultProcessor>, ResultProcessorConfig> builder = ImmutableMap
                .builder();
        for (Entry<String, Class<? extends ResultProcessor>> entry : processorToClass.entrySet()) {
            builder.put(entry.getValue(), getResultProcessorConfig(resultsProperties, entry.getKey()));
        }
        return builder.build();
    }

    public ImmutableMap<String, String> properties() {
        return properties;
    }

    /**
     * Gets the device configuration for the device specified on the command line, if any. If no
     * device was specified on the command line, the special name "default" will be present in the
     * options, and we'll choose a default device based on whether or not --worker-classpath-android
     * was provided on the command line.
     */
    public DeviceConfig getDeviceConfig(CaliperOptions options) {
        String deviceName = options.deviceName();
        ImmutableMap<String, String> devices = subgroupMap(properties, "device");

        if (deviceName.equals("default")) {
            if (!subgroupMap(devices, deviceName).isEmpty()) {
                // User had explicit configuration for a device named "default"
                throw new InvalidConfigurationException(
                        "device name 'default' is reserved; please use a different device name");
            }

            boolean hasAndroidClasspath = options.workerClasspath("android").isPresent();
            boolean hasJvmClasspath = options.workerClasspath("jvm").isPresent();

            if (hasAndroidClasspath && !hasJvmClasspath) {
                return getDeviceConfig("android");
            } else {
                return getDeviceConfig("local");
            }
        } else {
            return getDeviceConfig(deviceName);
        }
    }

    /** Returns the configuration for the device with the given name. */
    public DeviceConfig getDeviceConfig(String deviceName) {
        ImmutableMap<String, String> devices = subgroupMap(properties, "device");
        ImmutableMap<String, String> device = subgroupMap(devices, deviceName);

        String deviceTypeField = device.get("type");
        if (deviceTypeField == null) {
            throw new InvalidConfigurationException("Missing configuration field: device." + deviceName + ".type");
        }

        return DeviceConfig.builder().name(deviceName).type(DeviceType.of(deviceTypeField))
                .options(subgroupMap(device, "options")).build();
    }

    /** Returns the vm.args configuration values. */
    public List<String> getVmArgs() {
        return getArgs(subgroupMap(properties, "vm"));
    }

    public VmConfig getVmConfig(String name) {
        checkNotNull(name);
        ImmutableMap<String, String> vmGroupMap = subgroupMap(properties, "vm");
        ImmutableMap<String, String> vmMap = subgroupMap(vmGroupMap, name);
        VmConfig.Builder builder = VmConfig.builder().name(name);
        String type = vmMap.get("type");
        if (type != null) {
            builder.type(VmType.of(type));
        }
        String home = vmMap.get("home");
        if (home != null) {
            builder.home(home);
        }
        String executable = vmMap.get("executable");
        if (executable != null) {
            builder.executable(executable);
        }
        return builder.addAllArgs(getVmArgs()).addAllArgs(getArgs(vmMap)).build();
    }

    private static final Pattern INSTRUMENT_CLASS_PATTERN = Pattern.compile("([^\\.]+)\\.class");

    /**
     * Returns the default set of instruments to use if the user doesn't specify which instruments to
     * use on the command line.
     */
    public ImmutableSet<String> getDefaultInstruments() {
        // TODO(cgdecker): could/should this "defaults" be generalized?
        // e.g. if there's a command line option "--foo", "defaults.foo" is the default value of "foo"
        // if the user doesn't pass that option. This is already the case here since "--instrument" is
        // the long-form commmand line option, but I'm not trying to generalize now since there's no
        // apparent need to.
        ImmutableMap<String, String> defaults = subgroupMap(properties, "defaults");
        if (!defaults.isEmpty()) {
            String instruments = defaults.get("instrument");
            if (instruments != null) {
                return ImmutableSet.copyOf(Splitter.on(',').split(instruments));
            }
        }

        throw new InvalidConfigurationException(
                "Could not find default set of instruments to use (defaults.instrument in config file)");
    }

    public ImmutableSet<String> getConfiguredInstruments() {
        ImmutableSet.Builder<String> resultBuilder = ImmutableSet.builder();
        for (String key : subgroupMap(properties, "instrument").keySet()) {
            Matcher matcher = INSTRUMENT_CLASS_PATTERN.matcher(key);
            if (matcher.matches()) {
                resultBuilder.add(matcher.group(1));
            }
        }
        return resultBuilder.build();
    }

    public InstrumentConfig getInstrumentConfig(String name) {
        checkNotNull(name);
        ImmutableMap<String, String> instrumentGroupMap = subgroupMap(properties, "instrument");
        ImmutableMap<String, String> instrumentMap = subgroupMap(instrumentGroupMap, name);
        @Nullable
        String className = instrumentMap.get("class");
        checkArgument(className != null, "no instrument configured named %s", name);
        return new InstrumentConfig.Builder().className(className)
                .addAllOptions(subgroupMap(instrumentMap, "options")).build();
    }

    public ImmutableSet<Class<? extends ResultProcessor>> getConfiguredResultProcessors() {
        return resultProcessorConfigs.keySet();
    }

    public ResultProcessorConfig getResultProcessorConfig(Class<? extends ResultProcessor> resultProcessorClass) {
        return resultProcessorConfigs.get(resultProcessorClass);
    }

    private static ResultProcessorConfig getResultProcessorConfig(ImmutableMap<String, String> resultsProperties,
            String name) {
        ImmutableMap<String, String> resultsMap = subgroupMap(resultsProperties, name);
        return new ResultProcessorConfig.Builder().className(resultsMap.get("class"))
                .addAllOptions(subgroupMap(resultsMap, "options")).build();
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this).add("properties", properties).toString();
    }

    private static List<String> getArgs(Map<String, String> properties) {
        String argsString = Strings.nullToEmpty(properties.get("args"));
        ImmutableList.Builder<String> args = ImmutableList.builder();
        StringBuilder arg = new StringBuilder();
        for (int i = 0; i < argsString.length(); i++) {
            char c = argsString.charAt(i);
            switch (c) {
            case '\\':
                arg.append(argsString.charAt(++i));
                break;
            case ' ':
                if (arg.length() > 0) {
                    args.add(arg.toString());
                }
                arg = new StringBuilder();
                break;
            default:
                arg.append(c);
                break;
            }
        }
        if (arg.length() > 0) {
            args.add(arg.toString());
        }
        return args.build();
    }
}