1 /*
   2  * Copyright (c) 2013, 2018, 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  */
  23 
  24 
  25 package org.graalvm.compiler.core.test;
  26 
  27 import java.lang.annotation.RetentionPolicy;
  28 import java.lang.reflect.Field;
  29 import java.lang.reflect.Modifier;
  30 import java.lang.reflect.ParameterizedType;
  31 import java.lang.reflect.Type;
  32 import java.util.Iterator;
  33 import java.util.Map;
  34 import java.util.Properties;
  35 
  36 import jdk.internal.vm.compiler.collections.EconomicMap;
  37 import jdk.internal.vm.compiler.collections.MapCursor;
  38 import org.graalvm.compiler.options.Option;
  39 import org.graalvm.compiler.options.OptionDescriptor;
  40 import org.graalvm.compiler.options.OptionDescriptors;
  41 import org.graalvm.compiler.options.OptionKey;
  42 import org.graalvm.compiler.options.OptionType;
  43 import org.graalvm.compiler.options.OptionValues;
  44 
  45 /**
  46  * An implementation of {@link OptionDescriptor} that uses reflection to create descriptors from a
  47  * list of field name and help text pairs. We cannot use the {@link Option} annotation as it has a
  48  * {@link RetentionPolicy#SOURCE} retention policy.
  49  *
  50  * This class is useful for working with {@link OptionKey} and {@link OptionValues} but without
  51  * having to rely on {@link Option} and its associated annotation processor.
  52  */
  53 public class ReflectionOptionDescriptors implements OptionDescriptors {
  54 
  55     /**
  56      * Extracts name/value entries from a set of properties based on a given name prefix.
  57      *
  58      * @param properties the properties set to extract from
  59      * @param prefix entries whose names start with this prefix are extracted
  60      * @param stripPrefix specifies whether to remove the prefix from the names in the returned map
  61      */
  62     public static EconomicMap<String, String> extractEntries(Properties properties, String prefix, boolean stripPrefix) {
  63         EconomicMap<String, String> matches = EconomicMap.create();
  64         for (Map.Entry<Object, Object> e : properties.entrySet()) {
  65             String name = (String) e.getKey();
  66             if (name.startsWith(prefix)) {
  67                 String value = (String) e.getValue();
  68                 if (stripPrefix) {
  69                     name = name.substring(prefix.length());
  70                 }
  71                 matches.put(name, value);
  72             }
  73         }
  74         return matches;
  75     }
  76 
  77     private final EconomicMap<String, OptionDescriptor> descriptors = EconomicMap.create();
  78 
  79     public ReflectionOptionDescriptors(Class<?> declaringClass, String... fieldsAndHelp) {
  80         assert fieldsAndHelp.length % 2 == 0;
  81         for (int i = 0; i < fieldsAndHelp.length; i += 2) {
  82             String fieldName = fieldsAndHelp[i];
  83             String help = fieldsAndHelp[i + 1];
  84             addOption(declaringClass, fieldName, help);
  85         }
  86     }
  87 
  88     public ReflectionOptionDescriptors(Class<?> declaringClass, EconomicMap<String, String> fieldsAndHelp) {
  89         MapCursor<String, String> cursor = fieldsAndHelp.getEntries();
  90         while (cursor.advance()) {
  91             String fieldName = cursor.getKey();
  92             String help = cursor.getValue();
  93             addOption(declaringClass, fieldName, help);
  94         }
  95     }
  96 
  97     private void addOption(Class<?> declaringClass, String fieldName, String help) {
  98         try {
  99             Field f = declaringClass.getDeclaredField(fieldName);
 100             if (!OptionKey.class.isAssignableFrom(f.getType())) {
 101                 throw new IllegalArgumentException(String.format("Option field must be of type %s: %s", OptionKey.class.getName(), f));
 102             }
 103             if (!Modifier.isStatic(f.getModifiers())) {
 104                 throw new IllegalArgumentException(String.format("Option field must be static: %s", f));
 105             }
 106             f.setAccessible(true);
 107             Type declaredType = f.getAnnotatedType().getType();
 108             if (!(declaredType instanceof ParameterizedType)) {
 109                 throw new IllegalArgumentException(String.format("Option field must have a parameterized type: %s", f));
 110             }
 111             ParameterizedType pt = (ParameterizedType) declaredType;
 112             Type[] actualTypeArguments = pt.getActualTypeArguments();
 113             assert actualTypeArguments.length == 1;
 114             Class<?> optionValueType = (Class<?>) actualTypeArguments[0];
 115             descriptors.put(fieldName, OptionDescriptor.create(fieldName, OptionType.Debug, optionValueType, help, declaringClass, fieldName, (OptionKey<?>) f.get(null)));
 116         } catch (IllegalAccessException | NoSuchFieldException e) {
 117             throw new IllegalArgumentException(e);
 118         }
 119     }
 120 
 121     @Override
 122     public Iterator<OptionDescriptor> iterator() {
 123         return descriptors.getValues().iterator();
 124     }
 125 
 126     @Override
 127     public OptionDescriptor get(String value) {
 128         return descriptors.get(value);
 129     }
 130 }