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.options; 26 27 import java.io.PrintStream; 28 import java.util.ArrayList; 29 import java.util.Comparator; 30 import java.util.List; 31 import java.util.Map; 32 import java.util.SortedMap; 33 import java.util.TreeMap; 34 35 import jdk.internal.vm.compiler.collections.EconomicMap; 36 import jdk.internal.vm.compiler.collections.Equivalence; 37 import jdk.internal.vm.compiler.collections.UnmodifiableEconomicMap; 38 import jdk.internal.vm.compiler.collections.UnmodifiableMapCursor; 39 40 /** 41 * A context for obtaining values for {@link OptionKey}s. 42 */ 43 public class OptionValues { 44 45 private final UnmodifiableEconomicMap<OptionKey<?>, Object> values; 46 47 protected boolean containsKey(OptionKey<?> key) { 48 return values.containsKey(key); 49 } 50 51 public OptionValues(OptionValues initialValues, UnmodifiableEconomicMap<OptionKey<?>, Object> extraPairs) { 52 EconomicMap<OptionKey<?>, Object> map = newOptionMap(); 53 if (initialValues != null) { 54 map.putAll(initialValues.getMap()); 55 } 56 initMap(map, extraPairs); 57 this.values = map; 58 } 59 60 public OptionValues(OptionValues initialValues, OptionKey<?> key1, Object value1, Object... extraPairs) { 61 this(initialValues, asMap(key1, value1, extraPairs)); 62 } 63 64 /** 65 * Creates a new map suitable for using {@link OptionKey}s as keys. 66 */ 67 public static EconomicMap<OptionKey<?>, Object> newOptionMap() { 68 return EconomicMap.create(Equivalence.IDENTITY); 69 } 70 71 /** 72 * Gets an immutable view of the key/value pairs in this object. Values read from this view 73 * should be {@linkplain #decodeNull(Object) decoded} before being used. 74 */ 75 public UnmodifiableEconomicMap<OptionKey<?>, Object> getMap() { 76 return values; 77 } 78 79 /** 80 * @param key1 first key in map 81 * @param value1 first value in map 82 * @param extraPairs key/value pairs of the form {@code [key1, value1, key2, value2, ...]} 83 * @return a map containing the key/value pairs as entries 84 */ 85 public static EconomicMap<OptionKey<?>, Object> asMap(OptionKey<?> key1, Object value1, Object... extraPairs) { 86 EconomicMap<OptionKey<?>, Object> map = newOptionMap(); 87 map.put(key1, value1); 88 for (int i = 0; i < extraPairs.length; i += 2) { 89 OptionKey<?> key = (OptionKey<?>) extraPairs[i]; 90 Object value = extraPairs[i + 1]; 91 map.put(key, value); 92 } 93 return map; 94 } 95 96 public OptionValues(UnmodifiableEconomicMap<OptionKey<?>, Object> values) { 97 EconomicMap<OptionKey<?>, Object> map = newOptionMap(); 98 initMap(map, values); 99 this.values = map; 100 } 101 102 protected static void initMap(EconomicMap<OptionKey<?>, Object> map, UnmodifiableEconomicMap<OptionKey<?>, Object> values) { 103 UnmodifiableMapCursor<OptionKey<?>, Object> cursor = values.getEntries(); 104 while (cursor.advance()) { 105 map.put(cursor.getKey(), encodeNull(cursor.getValue())); 106 } 107 } 108 109 protected <T> T get(OptionKey<T> key) { 110 return get(values, key); 111 } 112 113 @SuppressWarnings("unchecked") 114 protected static <T> T get(UnmodifiableEconomicMap<OptionKey<?>, Object> values, OptionKey<T> key) { 115 Object value = values.get(key); 116 if (value == null) { 117 return key.getDefaultValue(); 118 } 119 return (T) decodeNull(value); 120 } 121 122 private static final Object NULL = new Object(); 123 124 protected static Object encodeNull(Object value) { 125 return value == null ? NULL : value; 126 } 127 128 /** 129 * Decodes a value that may be the sentinel value for {@code null} in a map. 130 */ 131 public static Object decodeNull(Object value) { 132 return value == NULL ? null : value; 133 } 134 135 @Override 136 public String toString() { 137 return toString(getMap()); 138 } 139 140 public static String toString(UnmodifiableEconomicMap<OptionKey<?>, Object> values) { 141 Comparator<OptionKey<?>> comparator = new Comparator<OptionKey<?>>() { 142 @Override 143 public int compare(OptionKey<?> o1, OptionKey<?> o2) { 144 return o1.getName().compareTo(o2.getName()); 145 } 146 }; 147 SortedMap<OptionKey<?>, Object> sorted = new TreeMap<>(comparator); 148 UnmodifiableMapCursor<OptionKey<?>, Object> cursor = values.getEntries(); 149 while (cursor.advance()) { 150 sorted.put(cursor.getKey(), decodeNull(cursor.getValue())); 151 } 152 return sorted.toString(); 153 } 154 155 private static final int PROPERTY_LINE_WIDTH = 80; 156 private static final int PROPERTY_HELP_INDENT = 10; 157 158 /** 159 * Wraps some given text to one or more lines of a given maximum width. 160 * 161 * @param text text to wrap 162 * @param width maximum width of an output line, exception for words in {@code text} longer than 163 * this value 164 * @return {@code text} broken into lines 165 */ 166 private static List<String> wrap(String text, int width) { 167 List<String> lines = new ArrayList<>(); 168 if (text.length() > width) { 169 String[] chunks = text.split("\\s+"); 170 StringBuilder line = new StringBuilder(); 171 for (String chunk : chunks) { 172 if (line.length() + chunk.length() > width) { 173 lines.add(line.toString()); 174 line.setLength(0); 175 } 176 if (line.length() != 0) { 177 line.append(' '); 178 } 179 line.append(chunk); 180 } 181 if (line.length() != 0) { 182 lines.add(line.toString()); 183 } 184 } else { 185 lines.add(text); 186 } 187 return lines; 188 } 189 190 /** 191 * Prints a help message to {@code out} describing all options available via {@code loader}. The 192 * key/value for each option is separated by {@code :=} if the option has an entry in this 193 * object otherwise {@code =} is used as the separator. 194 * 195 * @param loader 196 * @param out 197 * @param namePrefix 198 */ 199 public void printHelp(Iterable<OptionDescriptors> loader, PrintStream out, String namePrefix) { 200 SortedMap<String, OptionDescriptor> sortedOptions = new TreeMap<>(); 201 for (OptionDescriptors opts : loader) { 202 for (OptionDescriptor desc : opts) { 203 String name = desc.getName(); 204 OptionDescriptor existing = sortedOptions.put(name, desc); 205 assert existing == null || existing == desc : "Option named \"" + name + "\" has multiple definitions: " + existing.getLocation() + " and " + desc.getLocation(); 206 } 207 } 208 for (Map.Entry<String, OptionDescriptor> e : sortedOptions.entrySet()) { 209 OptionDescriptor desc = e.getValue(); 210 Object value = desc.getOptionKey().getValue(this); 211 if (value instanceof String) { 212 value = '"' + String.valueOf(value) + '"'; 213 } 214 215 String name = namePrefix + e.getKey(); 216 String assign = containsKey(desc.getOptionKey()) ? ":=" : "="; 217 String typeName = desc.getOptionKey() instanceof EnumOptionKey ? "String" : desc.getOptionValueType().getSimpleName(); 218 String linePrefix = String.format("%s %s %s ", name, assign, value); 219 int typeStartPos = PROPERTY_LINE_WIDTH - typeName.length(); 220 int linePad = typeStartPos - linePrefix.length(); 221 if (linePad > 0) { 222 out.printf("%s%-" + linePad + "s[%s]%n", linePrefix, "", typeName); 223 } else { 224 out.printf("%s[%s]%n", linePrefix, typeName); 225 } 226 227 List<String> helpLines; 228 String help = desc.getHelp(); 229 if (help.length() != 0) { 230 helpLines = wrap(help, PROPERTY_LINE_WIDTH - PROPERTY_HELP_INDENT); 231 helpLines.addAll(desc.getExtraHelp()); 232 } else { 233 helpLines = desc.getExtraHelp(); 234 } 235 for (String line : helpLines) { 236 out.printf("%" + PROPERTY_HELP_INDENT + "s%s%n", "", line); 237 } 238 } 239 } 240 }