1 /* 2 * Copyright (c) 2015, 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 package jdk.test.lib.jittester.utils; 25 26 import java.io.FileReader; 27 import java.io.IOException; 28 import java.util.*; 29 30 public class OptionResolver { 31 private final Map<String, Option<?>> options = new LinkedHashMap<>(20); 32 private Map<Option<?>, Object> values = Collections.emptyMap(); 33 34 public OptionResolver() { 35 } 36 37 public final void parse(String[] argv) { 38 parse(argv, null); 39 } 40 41 public final void parse(String[] argv, Option<String> propertyFileOption) { 42 int position = 0; 43 this.values = new HashMap<>(argv.length / 2); 44 45 while (position < argv.length) { 46 String curArg = argv[position]; 47 if (curArg.startsWith("-")) { 48 String valueArg = null; 49 int opt; 50 if (curArg.startsWith("--")) { 51 opt = curArg.indexOf("="); 52 if (opt != -1) { 53 valueArg = curArg.substring(opt + 1); 54 curArg = curArg.substring(0, opt); 55 } 56 } else if (curArg.length() > 2) { 57 for (opt = 1; opt < curArg.length(); ++opt) { 58 final char key = curArg.charAt(opt); 59 Option<?> flagOption = this.options.get("-" + key); 60 61 if (!flagOption.isFlag()) { 62 throw new IllegalArgumentException("Unknown flag option " + key); 63 } 64 65 values.put(flagOption, Boolean.TRUE); 66 } 67 68 ++position; 69 continue; 70 } 71 72 Option<?> currentOption = this.options.get(curArg); 73 if (currentOption == null) { 74 throw new IllegalArgumentException("Unknown option " + curArg); 75 } 76 77 Object value; 78 if (!currentOption.isFlag()) { 79 if (valueArg == null) { 80 ++position; 81 if (position < argv.length) { 82 valueArg = argv[position]; 83 } 84 } 85 } 86 try { 87 value = currentOption.parseFromString(valueArg); 88 } catch (Exception ex) { 89 throw new IllegalArgumentException("Error parsing " + valueArg + ", option " + curArg, ex); 90 } 91 values.put(currentOption, value); 92 } 93 ++position; 94 } 95 96 if (propertyFileOption != null && values.containsKey(propertyFileOption)) { 97 parseProperties(propertyFileOption.value()); 98 } 99 } 100 101 private void parseProperties(String fileName) { 102 Properties properties = new Properties(); 103 try { 104 properties.load(new FileReader(fileName)); 105 } catch (IOException e) { 106 throw new RuntimeException(e); 107 } 108 for (String optionName : properties.stringPropertyNames()) { 109 Option<?> currentOption = this.options.get("--" + optionName); 110 if (currentOption == null) { 111 throw new IllegalArgumentException("Unknown option in property file" + optionName); 112 } 113 114 final String propertyValue = properties.getProperty(optionName); 115 try { 116 values.putIfAbsent(currentOption, currentOption.parseFromString(propertyValue)); 117 } catch (Exception ex) { 118 throw new IllegalArgumentException("Error parsing " + propertyValue + ", property " + optionName, ex); 119 } 120 } 121 } 122 123 public Option<Integer> addIntegerOption(Character key, String name, int defaultValue, String description) { 124 final Option<Integer> option = new IntOption(key, name, defaultValue, description); 125 register(option); 126 return option; 127 } 128 129 public Option<Long> addLongOption(Character key, String name, long defaultValue, String description) { 130 final Option<Long> option = new LongOption(key, name, defaultValue, description); 131 register(option); 132 return option; 133 } 134 135 public Option<String> addStringOption(Character key, String name, String defaultValue, String description) { 136 final Option<String> option = new StringOption(key, name, defaultValue, description); 137 register(option); 138 return option; 139 } 140 141 public Option<Boolean> addBooleanOption(Character key, String name, boolean defaultValue, String description) { 142 final Option<Boolean> option = new BooleanOption(key, name, defaultValue, description); 143 register(option); 144 return option; 145 } 146 147 public Option<Integer> addIntegerOption(String name, int defaultValue, String description) { 148 return addIntegerOption(null, name, defaultValue, description); 149 } 150 151 public Option<String> addStringOption(String name, String defaultValue, String description) { 152 return addStringOption(null, name, defaultValue, description); 153 } 154 155 public Option<Boolean> addBooleanOption(String name, String description) { 156 return addBooleanOption(null, name, false, description); 157 } 158 159 private void register(Option<?> option) { 160 if (options.put("--" + option.longName, option) != null) { 161 throw new RuntimeException("Option is already registered for key " + option.longName); 162 } 163 if (option.shortName != null && options.put("-" + option.shortName, option) != null) { 164 throw new RuntimeException("Option is already registered for key " + option.shortName); 165 } 166 } 167 168 public abstract class Option<T> { 169 protected final Character shortName; 170 protected final String longName; 171 protected final T defaultValue; 172 protected final String description; 173 174 public Option(Character shortName, String longName, T defaultValue, String description) { 175 this.shortName = shortName; 176 this.longName = longName; 177 this.defaultValue = defaultValue; 178 this.description = description; 179 } 180 181 public Character getShortName() { 182 return shortName; 183 } 184 185 public String getLongName() { 186 return longName; 187 } 188 189 public T getDefaultValue() { 190 return defaultValue; 191 } 192 193 public String getDescription() { 194 return description; 195 } 196 197 @SuppressWarnings("unchecked") 198 public T value() { 199 return (T) values.getOrDefault(this, defaultValue); 200 } 201 202 public boolean isSet() { 203 return values.containsKey(this); 204 } 205 206 public boolean isFlag() { 207 return false; 208 } 209 210 public abstract T parseFromString(String arg); 211 } 212 213 private class StringOption extends Option<String> { 214 215 StringOption(Character s, String l, String v, String d) { 216 super(s, l, v, d); 217 } 218 219 @Override 220 public String parseFromString(String arg) { 221 return arg; 222 } 223 } 224 225 private class LongOption extends Option<Long> { 226 227 LongOption(Character s, String l, long v, String d) { 228 super(s, l, v, d); 229 } 230 231 @Override 232 public Long parseFromString(String arg) { 233 return Long.valueOf(arg); 234 } 235 } 236 237 private class IntOption extends Option<Integer> { 238 239 IntOption(Character s, String l, int v, String d) { 240 super(s, l, v, d); 241 } 242 243 @Override 244 public Integer parseFromString(String arg) { 245 return Integer.valueOf(arg); 246 } 247 } 248 249 private class BooleanOption extends Option<Boolean> { 250 251 BooleanOption(Character s, String l, boolean v, String d) { 252 super(s, l, v, d); 253 } 254 255 @Override 256 public boolean isFlag() { 257 return true; 258 } 259 260 @Override 261 public Boolean parseFromString(String arg) { 262 //null and empty value is considered true, as option is flag and value could be absent 263 return arg == null || "".equals(arg) || "1".equalsIgnoreCase(arg) || "true".equalsIgnoreCase(arg); 264 } 265 } 266 267 public Collection<Option<?>> getRegisteredOptions() { 268 return Collections.unmodifiableSet(new LinkedHashSet<>(options.values())); 269 } 270 }