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