Java tutorial
/* * Copyright (C) 2018 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.core; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import com.google.auto.value.AutoValue; import com.google.caliper.Param; import com.google.caliper.api.VmOptions; import com.google.caliper.util.InvalidCommandException; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Ordering; import com.google.common.collect.SetMultimap; import com.google.common.collect.Sets; import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; /** * A simplified model of a benchmark class, containing no reflective references to the class and * only those things that the runner needs to know about the class in order to determine the * scenarios to be run. */ @AutoValue public abstract class BenchmarkClassModel implements Serializable { // TODO(b/64154339): Don't use Java serialization for this private static final long serialVersionUID = 1; /** * Creates a model of the given benchmark class and does some validation of it and the given * user-provided values for the class' parameter fields. */ public static BenchmarkClassModel create(Class<?> clazz) { if (!clazz.getSuperclass().equals(Object.class)) { throw new InvalidBenchmarkException( "Class '%s' must not extend any class other than %s. Prefer composition.", clazz, Object.class); } if (Modifier.isAbstract(clazz.getModifiers())) { throw new InvalidBenchmarkException("Class '%s' is abstract", clazz); } BenchmarkClassModel.Builder builder = new AutoValue_BenchmarkClassModel.Builder().setName(clazz.getName()) .setSimpleName(clazz.getSimpleName()); for (Method method : clazz.getDeclaredMethods()) { builder.methodsBuilder().add(MethodModel.of(method)); } for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(Param.class)) { builder.parametersBuilder().put(field.getName(), Parameters.validateAndGetDefaults(field)); } } VmOptions vmOptions = clazz.getAnnotation(VmOptions.class); if (vmOptions != null) { builder.vmOptionsBuilder().add(vmOptions.value()); } return builder.build(); } /** * Validates the given user-provided parameters against the parameter fields on the benchmark * class. */ public static void validateUserParameters(Class<?> clazz, SetMultimap<String, String> userParameters) { for (String paramName : userParameters.keySet()) { try { Field field = clazz.getDeclaredField(paramName); Parameters.validate(field, userParameters.get(paramName)); } catch (NoSuchFieldException e) { throw new InvalidCommandException("unrecognized parameter: " + paramName); } catch (InvalidBenchmarkException e) { // TODO(kevinb): this is weird. throw new InvalidCommandException(e.getMessage()); } } } /** Returns the fully qualified name of the benchmark class. */ public abstract String name(); /** Returns the simple name of the benchmark class. */ public abstract String simpleName(); /** Returns the set of all methods declared on the class. */ public abstract ImmutableSet<MethodModel> methods(); /** * Returns the set of parameter fields for the class and their default values as a map from field * name to set of default values. */ // NOTE: Not a multimap because parameters with no default values are legal, and multimap can't // include a key mapped to nothing. abstract ImmutableMap<String, ImmutableSet<String>> parameters(); /** * Returns a multimap containing the full set of parameter values to use for the benchmark. For * parameters on the benchmark that have values in the given user-supplied parameters, the user's * specified values are used. For all other parameters, the default values specified in the * annotation or implied by the type are used. * * @throws IllegalArgumentException if a parameter for the benchmark has neither user-specified * values nor default values */ public final ImmutableSetMultimap<String, String> fillInDefaultParameterValues( ImmutableSetMultimap<String, String> userParameters) { ImmutableSetMultimap.Builder<String, String> combined = ImmutableSetMultimap.builder(); // For user parameters, this'll actually be the same as parameters().keySet(), since any extras // given at the command line are treated as errors; for VM parameters this is not the case. for (String name : Sets.union(parameters().keySet(), userParameters.keySet())) { ImmutableSet<String> values = userParameters.containsKey(name) ? userParameters.get(name) : parameters().get(name); combined.putAll(name, values); checkArgument(!values.isEmpty(), "ERROR: No default value provided for %s", name); } return combined.orderKeysBy(Ordering.natural()).build(); } /** Returns the set of VM options specified with the {@code @VmOptions} annotation. */ public abstract ImmutableSet<String> vmOptions(); @Override public String toString() { return name(); } /** Builder for {@link BenchmarkClassModel} instances. */ @AutoValue.Builder public abstract static class Builder { /** Sets the name of the benchmark class. */ abstract Builder setName(String className); /** Sets the simple name of the benchmark class. */ abstract Builder setSimpleName(String simpleName); /** Returns a builder for adding to the set of methods on the class. */ abstract ImmutableSet.Builder<MethodModel> methodsBuilder(); /** Returns a builder for adding to the map of parameters for the class. */ public abstract ImmutableMap.Builder<String, ImmutableSet<String>> parametersBuilder(); /** Sets the VM options to use for benchmarking the class. */ public abstract Builder setVmOptions(Iterable<String> vmOptions); /** Returns a builder for adding to the set of VM options for the class. */ // NOTE: This also makes explicitly setting VM options optional, which is useful for tests. public abstract ImmutableSet.Builder<String> vmOptionsBuilder(); /** Returns a new {@link BenchmarkClassModel} with all the previously set values. */ public abstract BenchmarkClassModel build(); } /** Model of a method on a benchmark class. */ @AutoValue public abstract static class MethodModel implements Serializable { private static final long serialVersionUID = 1; /** Creates a new {@link MethodModel} representing the given method. */ public static MethodModel of(Method method) { MethodModel.Builder builder = new AutoValue_BenchmarkClassModel_MethodModel.Builder() .setName(method.getName()).setModifiers(method.getModifiers()) .setDeclaringClass(method.getDeclaringClass().getName()); if (!method.getReturnType().equals(void.class)) { builder.setReturnType(method.getReturnType().getName()); } for (Class<?> parameterType : method.getParameterTypes()) { builder.parameterTypesBuilder().add(parameterType.getName()); } for (Class<?> exceptionType : method.getExceptionTypes()) { builder.exceptionTypesBuilder().add(exceptionType.getName()); } for (Annotation annotation : method.getAnnotations()) { builder.annotationTypesBuilder().add(annotation.annotationType().getName()); } return builder.build(); } /** Returns the name of this method. */ public abstract String name(); /** Returns the modifiers for this method. */ public abstract int modifiers(); /** Returns the class that declared this method. */ public abstract String declaringClass(); /** Returns the method's return type, or absent for a {@code void} method. */ public abstract Optional<String> returnType(); /** Returns the method's parameter types. */ public abstract ImmutableList<String> parameterTypes(); /** Returns the types of exceptions the method declares that it may throw. */ public abstract ImmutableSet<String> exceptionTypes(); /** Returns the types of annotations that are present on the method. */ public abstract ImmutableSet<String> annotationTypes(); /** * Returns whether or not the set of {@link #annotationTypes()} contains an annotation with the * given type name. */ public final boolean isAnnotationPresent(String annotationTypeName) { checkNotNull(annotationTypeName); return annotationTypes().contains(annotationTypeName); } /** * Returns whether or not the set of {@link #annotationTypes()} contains an annotation of the * given type. */ public final boolean isAnnotationPresent(Class<?> annotationType) { return isAnnotationPresent(annotationType.getName()); } /** Builder for {@link MethodModel} instances. */ @AutoValue.Builder interface Builder { /** Sets the name of this method. */ Builder setName(String name); /** Sets the modifiers for this method. */ Builder setModifiers(int modifiers); /** Sets the class that declared this method. */ Builder setDeclaringClass(String declaringClass); /** Sets the method's return type. */ Builder setReturnType(String returnType); /** Returns a builder for adding the method's parameter types. */ ImmutableList.Builder<String> parameterTypesBuilder(); /** * Returns a builder for adding the types of exceptions the method declares that it may throw. */ ImmutableSet.Builder<String> exceptionTypesBuilder(); /** Returns a builder for adding the types of annotations that are present on the method. */ ImmutableSet.Builder<String> annotationTypesBuilder(); /** Builds the {@link MethodModel}. */ MethodModel build(); } } }