OptionKey.java revision 12968:4d8a004e5c6d
1/*
2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.options;
24
25import java.util.Formatter;
26
27import org.graalvm.util.EconomicMap;
28
29/**
30 * A key for an option. The value for an option is obtained from an {@link OptionValues} object.
31 */
32public class OptionKey<T> {
33
34    private final T defaultValue;
35
36    private OptionDescriptor descriptor;
37
38    public OptionKey(T defaultValue) {
39        this.defaultValue = defaultValue;
40    }
41
42    /**
43     * Sets the descriptor for this option.
44     */
45    public final void setDescriptor(OptionDescriptor descriptor) {
46        assert this.descriptor == null : "Overwriting existing descriptor";
47        this.descriptor = descriptor;
48    }
49
50    /**
51     * Returns the descriptor for this option, if it has been set by
52     * {@link #setDescriptor(OptionDescriptor)}.
53     */
54    public final OptionDescriptor getDescriptor() {
55        return descriptor;
56    }
57
58    /**
59     * Checks that a descriptor exists for this key after triggering loading of descriptors.
60     */
61    protected boolean checkDescriptorExists() {
62        OptionKey.Lazy.init();
63        if (descriptor == null) {
64            Formatter buf = new Formatter();
65            buf.format("Could not find a descriptor for an option key. The most likely cause is " +
66                            "a dependency on the %s annotation without a dependency on the " +
67                            "org.graalvm.compiler.options.processor.OptionProcessor annotation processor.", Option.class.getName());
68            StackTraceElement[] stackTrace = new Exception().getStackTrace();
69            if (stackTrace.length > 2 &&
70                            stackTrace[1].getClassName().equals(OptionKey.class.getName()) &&
71                            stackTrace[1].getMethodName().equals("getValue")) {
72                String caller = stackTrace[2].getClassName();
73                buf.format(" In suite.py, add GRAAL_OPTIONS_PROCESSOR to the \"annotationProcessors\" attribute of the project " +
74                                "containing %s.", caller);
75            }
76            throw new AssertionError(buf.toString());
77        }
78        return true;
79    }
80
81    /**
82     * Mechanism for lazily loading all available options which has the side effect of assigning
83     * names to the options.
84     */
85    static class Lazy {
86        static {
87            for (OptionDescriptors opts : OptionsParser.getOptionsLoader()) {
88                for (OptionDescriptor desc : opts) {
89                    desc.getName();
90                }
91            }
92        }
93
94        static void init() {
95            /* Running the static class initializer does all the initialization. */
96        }
97    }
98
99    /**
100     * Gets the name of this option. The name for an option value with a null
101     * {@linkplain #setDescriptor(OptionDescriptor) descriptor} is the value of
102     * {@link Object#toString()}.
103     */
104    public final String getName() {
105        if (descriptor == null) {
106            // Trigger initialization of OptionsLoader to ensure all option values have
107            // a descriptor which is required for them to have meaningful names.
108            Lazy.init();
109        }
110        return descriptor == null ? super.toString() : descriptor.getName();
111    }
112
113    @Override
114    public String toString() {
115        return getName();
116    }
117
118    /**
119     * The initial value specified in source code.
120     */
121    public final T getDefaultValue() {
122        return defaultValue;
123    }
124
125    /**
126     * Returns true if the option has been set in any way. Note that this doesn't mean that the
127     * current value is different than the default.
128     */
129    public boolean hasBeenSet(OptionValues values) {
130        return values.containsKey(this);
131    }
132
133    /**
134     * Gets the value of this option in {@code values}.
135     */
136    public T getValue(OptionValues values) {
137        assert checkDescriptorExists();
138        return values.get(this);
139    }
140
141    /**
142     * Sets the value of this option in a given map. The
143     * {@link #onValueUpdate(EconomicMap, Object, Object)} method is called once the value is set.
144     *
145     * @param values map of option values
146     * @param v the value to set for this key in {@code map}
147     */
148    @SuppressWarnings("unchecked")
149    public void update(EconomicMap<OptionKey<?>, Object> values, Object v) {
150        T oldValue = (T) values.put(this, v);
151        onValueUpdate(values, oldValue, (T) v);
152    }
153
154    /**
155     * Notifies this object when a value associated with this key is set or updated in
156     * {@code values}.
157     *
158     * @param values
159     * @param oldValue
160     * @param newValue
161     */
162    protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, T oldValue, T newValue) {
163    }
164}
165