1/* 2 * Copyright (C) 2011 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.caliper.runner; 18 19import com.google.caliper.Param; 20import com.google.caliper.util.Parser; 21import com.google.caliper.util.Parsers; 22import com.google.caliper.util.Util; 23import com.google.common.collect.ImmutableCollection; 24import com.google.common.collect.ImmutableList; 25import com.google.common.collect.ImmutableSet; 26import com.google.common.primitives.Primitives; 27 28import java.lang.reflect.Field; 29import java.text.ParseException; 30 31/** 32 * Represents an injectable parameter, marked with one of @Param, @VmParam. Has nothing to do with 33 * particular choices of <i>values</i> for this parameter (except that it knows how to find the 34 * <i>default</i> values). 35 */ 36public final class Parameter { 37 public static Parameter create(Field field) throws InvalidBenchmarkException { 38 return new Parameter(field); 39 } 40 41 private final Field field; 42 private final Parser<?> parser; 43 private final ImmutableList<String> defaults; 44 45 public Parameter(Field field) throws InvalidBenchmarkException { 46 if (Util.isStatic(field)) { 47 throw new InvalidBenchmarkException("Parameter field '%s' must not be static", 48 field.getName()); 49 } 50 if (RESERVED_NAMES.contains(field.getName())) { 51 throw new InvalidBenchmarkException("Class '%s' uses reserved parameter name '%s'", 52 field.getDeclaringClass(), field.getName()); 53 } 54 55 this.field = field; 56 field.setAccessible(true); 57 58 Class<?> type = Primitives.wrap(field.getType()); 59 try { 60 this.parser = Parsers.conventionalParser(type); 61 } catch (NoSuchMethodException e) { 62 throw new InvalidBenchmarkException("Type '%s' of parameter field '%s' has no recognized " 63 + "String-converting method; see <TODO> for details", type, field.getName()); 64 } 65 66 this.defaults = findDefaults(field); 67 validate(defaults); 68 } 69 70 void validate(ImmutableCollection<String> values) throws InvalidBenchmarkException { 71 for (String valueAsString : values) { 72 try { 73 parser.parse(valueAsString); 74 } catch (ParseException e) { 75 throw new InvalidBenchmarkException( 76 "Cannot convert value '%s' to type '%s': %s", 77 valueAsString, field.getType(), e.getMessage()); 78 } 79 } 80 } 81 82 static final ImmutableSet<String> RESERVED_NAMES = ImmutableSet.of( 83 "benchmark", 84 "environment", 85 "instrument", 86 "measurement", // e.g. runtime, allocation, etc. 87 "run", 88 "trial", // currently unused, but we might need it 89 "vm"); 90 91 String name() { 92 return field.getName(); 93 } 94 95 ImmutableList<String> defaults() { 96 return defaults; 97 } 98 99 void inject(Object benchmark, String value) { 100 try { 101 Object o = parser.parse(value); 102 field.set(benchmark, o); 103 } catch (ParseException impossible) { 104 // already validated both defaults and command-line 105 throw new AssertionError(impossible); 106 } catch (IllegalAccessException impossible) { 107 throw new AssertionError(impossible); 108 } 109 } 110 111 private static ImmutableList<String> findDefaults(Field field) { 112 String[] defaultsAsStrings = field.getAnnotation(Param.class).value(); 113 if (defaultsAsStrings.length > 0) { 114 return ImmutableList.copyOf(defaultsAsStrings); 115 } 116 117 Class<?> type = field.getType(); 118 if (type == boolean.class) { 119 return ALL_BOOLEANS; 120 } 121 122 if (type.isEnum()) { 123 ImmutableList.Builder<String> builder = ImmutableList.builder(); 124 for (Object enumConstant : type.getEnumConstants()) { 125 builder.add(enumConstant.toString()); 126 } 127 return builder.build(); 128 } 129 return ImmutableList.of(); 130 } 131 132 private static final ImmutableList<String> ALL_BOOLEANS = ImmutableList.of("true", "false"); 133} 134