aboutsummaryrefslogtreecommitdiff
path: root/src/com/google/caliper/Parameter.java
blob: a5ab6c8da0b26dd75009f49509a2a790bf923cc7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
 * Copyright (C) 2009 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;

import java.lang.reflect.*;
import java.util.*;

/**
 * A parameter in a {@link DefaultBenchmarkSuite}.
 */
abstract class Parameter<T> {

  private final Field field;

  private Parameter(Field field) {
    this.field = field;
  }

  /**
   * Returns all properties for the given class.
   */
  public static Map<String, Parameter<?>> forClass(Class<? extends BenchmarkSuite> suiteClass) {
    Map<String, Parameter<?>> parameters = new TreeMap<String, Parameter<?>>();
    for (final Field field : suiteClass.getDeclaredFields()) {
      if (field.isAnnotationPresent(Param.class)) {
        field.setAccessible(true);
        Parameter parameter = Parameter.forField(suiteClass, field);
        parameters.put(parameter.getName(), parameter);
      }
    }
    return parameters;
  }

  public static Parameter forField(
      Class<? extends BenchmarkSuite> suiteClass, final Field field) {
    Parameter result = null;
    Type returnType = null;
    Member member = null;

    try {
      final Method valuesMethod = suiteClass.getDeclaredMethod(field.getName() + "Values");
      valuesMethod.setAccessible(true);
      member = valuesMethod;
      returnType = valuesMethod.getGenericReturnType();
      result = new Parameter<Object>(field) {
        @SuppressWarnings("unchecked") // guarded below
        public Collection<Object> values() throws Exception {
          return (Collection<Object>) valuesMethod.invoke(null);
        }
      };
    } catch (NoSuchMethodException ignored) {
    }

    try {
      final Field valuesField = suiteClass.getDeclaredField(field.getName() + "Values");
      valuesField.setAccessible(true);
      member = valuesField;
      if (result != null) {
        throw new ConfigurationException("Two values members defined for " + field);
      }
      returnType = valuesField.getGenericType();
      result = new Parameter<Object>(field) {
        @SuppressWarnings("unchecked") // guarded below
        public Collection<Object> values() throws Exception {
          return (Collection<Object>) valuesField.get(null);
        }
      };
    } catch (NoSuchFieldException ignored) {
    }

    if (result == null) {
      throw new ConfigurationException("No values member defined for " + field);
    }

    if (!Modifier.isStatic(member.getModifiers())) {
      throw new ConfigurationException("Values member must be static " + member);
    }

    // validate return type
    boolean valid = false;
    if (returnType instanceof ParameterizedType) {
      ParameterizedType type = (ParameterizedType) returnType;
      if (type.getRawType() == Collection.class) {
        valid = true;
      }
    }

    if (!valid) {
      throw new ConfigurationException("Invalid return type " + returnType
          + " for values member " + member + "; must be Collection");
    }

    return result;
  }

  /**
   * Sets the value of this property to the specified value for the given suite.
   */
  public void set(BenchmarkSuite suite, Object value) throws Exception {
    field.set(suite, value);
  }

  /**
   * Returns the available values of the property as specified by the suite.
   */
  public abstract Collection<T> values() throws Exception;

  /**
   * Returns the parameter's type, such as double.class.
   */
  public Type getType() {
    return field.getGenericType();
  }

  /**
   * Returns the field's name.
   */
  public String getName() {
    return field.getName();
  }
}