blob: caca2523ea6fc55aafeea638ff3f061fb86fc5f1 [file] [log] [blame]
Jesse Wilson109c1282009-12-08 13:45:25 -08001/*
2 * Copyright (C) 2009 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;
18
Jesse Wilsonf062bf42010-01-13 17:12:18 -080019import java.lang.reflect.Field;
20import java.lang.reflect.Member;
21import java.lang.reflect.Method;
22import java.lang.reflect.Modifier;
23import java.lang.reflect.ParameterizedType;
24import java.lang.reflect.Type;
25import java.util.Arrays;
26import java.util.Collection;
27import java.util.Collections;
28import java.util.EnumSet;
29import java.util.Map;
30import java.util.Set;
31import java.util.TreeMap;
Jesse Wilson109c1282009-12-08 13:45:25 -080032
33/**
Jesse Wilson1440b362009-12-15 18:54:02 -080034 * A parameter in a {@link SimpleBenchmark}.
Jesse Wilson109c1282009-12-08 13:45:25 -080035 */
36abstract class Parameter<T> {
37
38 private final Field field;
39
40 private Parameter(Field field) {
41 this.field = field;
42 }
43
44 /**
45 * Returns all properties for the given class.
46 */
Jesse Wilson1440b362009-12-15 18:54:02 -080047 public static Map<String, Parameter<?>> forClass(Class<? extends Benchmark> suiteClass) {
Jesse Wilson109c1282009-12-08 13:45:25 -080048 Map<String, Parameter<?>> parameters = new TreeMap<String, Parameter<?>>();
Jesse Wilsonf062bf42010-01-13 17:12:18 -080049 for (Field field : suiteClass.getDeclaredFields()) {
Jesse Wilson109c1282009-12-08 13:45:25 -080050 if (field.isAnnotationPresent(Param.class)) {
51 field.setAccessible(true);
Jesse Wilsonf062bf42010-01-13 17:12:18 -080052 Parameter<?> parameter = forField(suiteClass, field);
Jesse Wilson109c1282009-12-08 13:45:25 -080053 parameters.put(parameter.getName(), parameter);
54 }
55 }
56 return parameters;
57 }
58
Jesse Wilsonf062bf42010-01-13 17:12:18 -080059 private static Parameter<?> forField(
Jesse Wilson1440b362009-12-15 18:54:02 -080060 Class<? extends Benchmark> suiteClass, final Field field) {
Jesse Wilsonf062bf42010-01-13 17:12:18 -080061 // First check for String values on the annotation itself
62 final Object[] defaults = field.getAnnotation(Param.class).value();
63 if (defaults.length > 0) {
64 return new Parameter<Object>(field) {
65 @Override public Collection<Object> values() throws Exception {
66 return Arrays.asList(defaults);
67 }
68 };
69 // TODO: or should we continue so we can give an error/warning if params are also give in a
70 // method or field?
71 }
72
73 Parameter<?> result = null;
Jesse Wilson109c1282009-12-08 13:45:25 -080074 Type returnType = null;
75 Member member = null;
76
Jesse Wilsonf062bf42010-01-13 17:12:18 -080077 // Now check for a fooValues() method
Jesse Wilson109c1282009-12-08 13:45:25 -080078 try {
79 final Method valuesMethod = suiteClass.getDeclaredMethod(field.getName() + "Values");
80 valuesMethod.setAccessible(true);
81 member = valuesMethod;
82 returnType = valuesMethod.getGenericReturnType();
83 result = new Parameter<Object>(field) {
84 @SuppressWarnings("unchecked") // guarded below
Jesse Wilsonf062bf42010-01-13 17:12:18 -080085 @Override public Collection<Object> values() throws Exception {
Jesse Wilson109c1282009-12-08 13:45:25 -080086 return (Collection<Object>) valuesMethod.invoke(null);
87 }
88 };
89 } catch (NoSuchMethodException ignored) {
90 }
91
Jesse Wilsonf062bf42010-01-13 17:12:18 -080092 // Now check for a fooValues field
Jesse Wilson109c1282009-12-08 13:45:25 -080093 try {
94 final Field valuesField = suiteClass.getDeclaredField(field.getName() + "Values");
95 valuesField.setAccessible(true);
96 member = valuesField;
97 if (result != null) {
98 throw new ConfigurationException("Two values members defined for " + field);
99 }
100 returnType = valuesField.getGenericType();
101 result = new Parameter<Object>(field) {
102 @SuppressWarnings("unchecked") // guarded below
Jesse Wilsonf062bf42010-01-13 17:12:18 -0800103 @Override public Collection<Object> values() throws Exception {
Jesse Wilson109c1282009-12-08 13:45:25 -0800104 return (Collection<Object>) valuesField.get(null);
105 }
106 };
107 } catch (NoSuchFieldException ignored) {
108 }
109
Jesse Wilsonf062bf42010-01-13 17:12:18 -0800110 if (member != null && !Modifier.isStatic(member.getModifiers())) {
111 throw new ConfigurationException("Values member must be static " + member);
112 }
113
114 // If there isn't a values member but the parameter is an enum, we default
115 // to EnumSet.allOf.
116 if (member == null && field.getType().isEnum()) {
117 returnType = Collection.class;
118 result = new Parameter<Object>(field) {
119 // TODO: figure out the simplest way to make this compile and be green in IDEA too
120 @SuppressWarnings({"unchecked", "RawUseOfParameterizedType", "RedundantCast"})
121 // guarded above
122 @Override public Collection<Object> values() throws Exception {
123 Set<Enum> set = EnumSet.allOf((Class<Enum>) field.getType());
124 return (Collection) set;
125 }
126 };
127 }
128
Jesse Wilson109c1282009-12-08 13:45:25 -0800129 if (result == null) {
Jesse Wilsonf062bf42010-01-13 17:12:18 -0800130 return new Parameter<Object>(field) {
131 @Override public Collection<Object> values() {
132 // TODO: need tests to make sure this fails properly when no cmdline params given and
133 // works properly when they are given
134 return Collections.emptySet();
135 }
136 };
137 } else if (!isValidReturnType(returnType)) {
Jesse Wilson109c1282009-12-08 13:45:25 -0800138 throw new ConfigurationException("Invalid return type " + returnType
139 + " for values member " + member + "; must be Collection");
140 }
Jesse Wilson109c1282009-12-08 13:45:25 -0800141 return result;
142 }
143
Jesse Wilsonf062bf42010-01-13 17:12:18 -0800144 private static boolean isValidReturnType(Type returnType) {
145 if (returnType == Collection.class) {
146 return true;
147 }
148 if (returnType instanceof ParameterizedType) {
149 ParameterizedType type = (ParameterizedType) returnType;
150 if (type.getRawType() == Collection.class) {
151 return true;
152 }
153 }
154 return false;
155 }
156
Jesse Wilson109c1282009-12-08 13:45:25 -0800157 /**
158 * Sets the value of this property to the specified value for the given suite.
159 */
Jesse Wilson1440b362009-12-15 18:54:02 -0800160 public void set(Benchmark suite, Object value) throws Exception {
Jesse Wilson109c1282009-12-08 13:45:25 -0800161 field.set(suite, value);
162 }
163
164 /**
165 * Returns the available values of the property as specified by the suite.
166 */
167 public abstract Collection<T> values() throws Exception;
168
169 /**
170 * Returns the parameter's type, such as double.class.
171 */
172 public Type getType() {
173 return field.getGenericType();
174 }
175
176 /**
177 * Returns the field's name.
178 */
Jesse Wilsonf062bf42010-01-13 17:12:18 -0800179 String getName() {
Jesse Wilson109c1282009-12-08 13:45:25 -0800180 return field.getName();
181 }
Jesse Wilsonf062bf42010-01-13 17:12:18 -0800182}