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