// Options.java package net.sf.gogui.util; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.Reader; import java.util.Map; import java.util.TreeMap; import java.util.ArrayList; /** Parser for command line options. Options begin with a single '-' character. */ public class Options { /** Parse options. @param args Command line args from main method @param specs Specification of allowed options. Contains option names (without '-'). Options that need an argument must have a ':' appended. The special argument '--' stops option parsing, all following arguments are treated as non-option arguments. @throws ErrorMessage If options are not valid according to specs. */ public Options(String[] args, String[] specs) throws ErrorMessage { for (String spec : specs) { if (spec.length() > 0) m_map.put(spec, null); } parseArgs(args); } /** Check if option is present. */ public boolean contains(String option) { String value = get(option, null); return (value != null); } /** Return string option value. @param option The option key. @return The option value or en empty string, if option is not present. */ public String get(String option) { return get(option, ""); } /** Return string option value. @param option The option key. @param defaultValue The default value. @return The option value or defaultValue, if option is not present. */ public String get(String option, String defaultValue) { assert isValidOption(option); String value = getValue(option); if (value == null) return defaultValue; return value; } /** Get remaining arguments that are not options. @return The sequence of non-option arguments. */ public ArrayList<String> getArguments() { return m_args; } /** Check that the number of non-option arguments is zero. @throws ErrorMessage If there are any non-option arguments. */ public void checkNoArguments() throws ErrorMessage { if (! m_args.isEmpty()) throw new ErrorMessage( "Command does not allow arguments that are not options"); } /** Parse double option. @param option The option key. @return The option value or 0, if option is not present. @throws ErrorMessage If option value is not a double. */ public double getDouble(String option) throws ErrorMessage { return getDouble(option, 0); } /** Parse double option. @param option The option key. @param defaultValue The default value. @return The option value or defaultValue, if option is not present. @throws ErrorMessage If option value is not a double. */ public double getDouble(String option, double defaultValue) throws ErrorMessage { String value = get(option, Double.toString(defaultValue)); if (value == null) return defaultValue; try { return Double.parseDouble(value); } catch (NumberFormatException e) { throw new ErrorMessage("Option -" + option + " needs float value"); } } /** Parse integer option. @param option The option key. @return The option value or 0, if option is not present. @throws ErrorMessage If option value is not an integer. */ public int getInteger(String option) throws ErrorMessage { return getInteger(option, 0); } /** Parse integer option. @param option The option key. @param defaultValue The default value. @return The option value or defaultValue, if option is not present. @throws ErrorMessage If option value is not an integer. */ public int getInteger(String option, int defaultValue) throws ErrorMessage { String value = get(option, Integer.toString(defaultValue)); if (value == null) return defaultValue; try { return Integer.parseInt(value); } catch (NumberFormatException e) { throw new ErrorMessage("Option -" + option + " needs integer value"); } } /** Parse integer option with range check. @param option The option key. @param defaultValue The default value. @param min The minimum valid value. @return The option value or defaultValue, if option is not present. @throws ErrorMessage If option value is less than min. */ public int getInteger(String option, int defaultValue, int min) throws ErrorMessage { int value = getInteger(option, defaultValue); if (value < min) throw new ErrorMessage("Option -" + option + " must be greater than " + min); return value; } /** Parse integer option with range check. @param option The option key. @param defaultValue The default value. @param min The minimum valid value. @param max The maximum valid value. @return The option value or defaultValue, if option is not present. @throws ErrorMessage If option value is less than min or greater than max. */ public int getInteger(String option, int defaultValue, int min, int max) throws ErrorMessage { int value = getInteger(option, defaultValue); if (value < min || value > max) throw new ErrorMessage("Option -" + option + " must be in [" + min + ".." + max + "]"); return value; } /** Parse long integer option. @param option The option key. @return The option value or 0, if option is not present. @throws ErrorMessage If option value is not a long integer. */ public long getLong(String option) throws ErrorMessage { return getLong(option, 0L); } /** Parse long integer option. @param option The option key. @param defaultValue The default value. @return The option value or defaultValue, if option is not present. @throws ErrorMessage If option value is not a long integer. */ public long getLong(String option, long defaultValue) throws ErrorMessage { String value = get(option, Long.toString(defaultValue)); if (value == null) return defaultValue; try { return Long.parseLong(value); } catch (NumberFormatException e) { throw new ErrorMessage("Option -" + option + " needs long integer value"); } } /** Read options from a file given with the option "config". Requires that "config" is an allowed option. @throws ErrorMessage If options in file are not valid according to the specification. */ public void handleConfigOption() throws ErrorMessage { if (! contains("config")) return; String filename = get("config"); InputStream inputStream; try { inputStream = new FileInputStream(filename); } catch (FileNotFoundException e) { throw new ErrorMessage("File not found: " + filename); } Reader reader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(reader); try { StringBuilder buffer = new StringBuilder(256); String line; while (true) { line = bufferedReader.readLine(); if (line == null) break; buffer.append(line); buffer.append(' '); } parseArgs(StringUtil.splitArguments(buffer.toString())); } catch (IOException e) { StringUtil.printException(e); } finally { try { bufferedReader.close(); } catch (IOException e) { StringUtil.printException(e); } } } /** Creates a new Options instance from command line. Automatically calls handleConfigOption. @param args The command line split into arguments. @param specs Option specification as in constructor. @return The new Options instance. @throws ErrorMessage If options are not valid according to specs. */ public static Options parse(String[] args, String[] specs) throws ErrorMessage { Options opt = new Options(args, specs); opt.handleConfigOption(); return opt; } private final ArrayList<String> m_args = new ArrayList<String>(); private final Map<String,String> m_map = new TreeMap<String,String>(); private String getSpec(String option) throws ErrorMessage { if (m_map.containsKey(option)) return option; else if (m_map.containsKey(option + ":")) return option + ":"; throw new ErrorMessage("Unknown option -" + option); } private String getValue(String option) { assert isValidOption(option); if (m_map.containsKey(option)) return m_map.get(option); return m_map.get(option + ":"); } private boolean isOptionKey(String s) { return (s.length() > 0 && s.charAt(0) == '-'); } private boolean isValidOption(String option) { return (m_map.containsKey(option) || m_map.containsKey(option + ":")); } private boolean needsValue(String spec) { return (spec.length() > 0 && spec.substring(spec.length() - 1).equals(":")); } private void parseArgs(String args[]) throws ErrorMessage { boolean stopParse = false; int n = 0; while (n < args.length) { String s = args[n]; ++n; if (s.equals("--")) { stopParse = true; continue; } if (isOptionKey(s) && ! stopParse) { String spec = getSpec(s.substring(1)); if (needsValue(spec)) { if (n >= args.length) throw new ErrorMessage("Option " + s + " needs value"); String value = args[n]; ++n; m_map.put(spec, value); } else m_map.put(spec, "1"); } else m_args.add(s); } } }