diff src/main/java/nl/cwi/monetdb/util/CmdLineOpts.java @ 0:a5a898f6886c

Copy of MonetDB java directory changeset e6e32756ad31.
author Sjoerd Mullender <sjoerd@acm.org>
date Wed, 21 Sep 2016 09:34:48 +0200 (2016-09-21)
parents
children 57978db4ee57 17fbaf2635bb
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/main/java/nl/cwi/monetdb/util/CmdLineOpts.java
@@ -0,0 +1,388 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0.  If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Copyright 1997 - July 2008 CWI, August 2008 - 2016 MonetDB B.V.
+ */
+
+package nl.cwi.monetdb.util;
+
+import java.util.*;
+import java.io.*;
+
+public class CmdLineOpts {
+	/** the arguments we handle */
+	private Map<String, OptionContainer> opts = new HashMap<String, OptionContainer>();
+	/** the options themself */
+	private List<OptionContainer> options = new ArrayList<OptionContainer>();
+	
+	/** no arguments */
+	public static int CAR_ZERO		= 0;
+	/** always one argument */
+	public static int CAR_ONE		= 1;
+	/** zero or one argument */
+	public static int CAR_ZERO_ONE	= 2;
+	/** zero or many arguments */
+	public static int CAR_ZERO_MANY	= 3;
+	/** one or many arguments */
+	public static int CAR_ONE_MANY	= 4;
+	
+	public CmdLineOpts() {
+	}
+
+	public void addOption(
+			String shorta,
+			String longa,
+			int type,
+			String defaulta,
+			String descriptiona)
+		throws OptionsException {
+		OptionContainer oc =
+			new OptionContainer(
+				shorta,
+				longa,
+				type,
+				defaulta,
+				descriptiona
+			);
+		if (shorta != null) opts.put(shorta, oc);
+		if (longa != null) opts.put(longa, oc);
+	}
+
+	public void removeOption(String name) {
+		OptionContainer oc = opts.get(name);
+		if (oc != null) {
+			opts.remove(oc.shorta);
+			opts.remove(oc.longa);
+		}
+	}
+
+	public OptionContainer getOption(String key) throws OptionsException {
+		OptionContainer ret = opts.get(key);
+		if (ret == null) throw
+			new OptionsException("No such option: " + key);
+
+		return ret;
+	}
+
+	public void processFile(File file) throws OptionsException {
+		// the file is there, parse it and set its settings
+		Properties prop = new Properties();
+		try {
+			FileInputStream in = new FileInputStream(file);
+			try {
+				prop.load(in);
+			} finally {
+				in.close();
+			}
+
+			for (Enumeration<?> e = prop.propertyNames(); e.hasMoreElements(); ) {
+				String key = (String) e.nextElement();
+				OptionContainer option = opts.get(key);
+				if (option == null) throw
+					new OptionsException("Unknown option: " + key);
+				option.resetArguments();
+				option.addArgument(prop.getProperty(key));
+			}
+		} catch (IOException e) {
+			// well... then forget about it
+		}
+	}
+
+	public void processArgs(String args[]) throws OptionsException {
+		// parse and set the command line arguments
+		OptionContainer option = null;
+		int quant = -1;
+		int qcount = 0;
+		boolean moreData = false;
+		for (int i = 0; i < args.length; i++) {
+			if (option == null) {
+				if (args[i].charAt(0) != '-') throw
+					new OptionsException("Unexpected value: " + args[i]);
+
+				// see what kind of argument we have
+				if (args[i].length() == 1)
+					throw new OptionsException("Illegal argument: '-'");
+				if (args[i].charAt(1) == '-') {
+					// we have a long argument
+					// since we don't accept inline values we can take
+					// everything left in the string as argument, unless
+					// there is a = in there...
+					String tmp = args[i].substring(2);
+					int pos = tmp.indexOf('=');
+					if (pos == -1) {
+						option = opts.get(tmp);
+						moreData = false;
+					} else {
+						option = opts.get(tmp.substring(0, pos));
+						// modify the option a bit so the code below
+						// handles the moreData correctly
+						args[i] = "-?" + tmp.substring(pos + 1);
+						moreData = true;
+					}
+				} else if (args[i].charAt(1) == 'X') {
+					// extra argument, same as long argument
+					String tmp = args[i].substring(1);
+					int pos = tmp.indexOf('=');
+					if (pos == -1) {
+						option = opts.get(tmp);
+						moreData = false;
+					} else {
+						option = opts.get(tmp.substring(0, pos));
+						// modify the option a bit so the code below
+						// handles the moreData correctly
+						args[i] = "-?" + tmp.substring(pos + 1);
+						moreData = true;
+					}
+				} else {
+					// single char argument
+					option = opts.get("" + args[i].charAt(1));
+					// is there more data left in the argument?
+					moreData = args[i].length() > 2 ? true : false;
+				}
+
+				if (option != null) {
+					// make sure we overwrite previously set arguments
+					option.resetArguments();
+					int card = option.getCardinality();
+					if (card == CAR_ONE) {
+						if (moreData) {
+							option.addArgument(args[i].substring(2));
+							option = null;
+						} else {
+							quant = 1;
+						}
+					} else if (card == CAR_ZERO_ONE) {
+						option.setPresent();
+						qcount = 1;
+						quant = 2;
+						if (moreData) {
+							option.addArgument(args[i].substring(2));
+							option = null;
+						}
+					} else if (card == CAR_ZERO_MANY) {
+						option.setPresent();
+						qcount = 1;
+						quant = -1;
+						if (moreData) {
+							option.addArgument(args[i].substring(2));
+							qcount++;
+						}
+					} else if (card == CAR_ZERO) {
+						option.setPresent();
+						option = null;
+					}
+				} else {
+					throw new OptionsException("Unknown argument: " + args[i]);
+				}
+			} else {
+				// store the `value'
+				option.addArgument(args[i]);
+				if (++qcount == quant) {
+					quant = 0;
+					qcount = 0;
+					option = null;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Returns a help message for all options loaded.
+	 *
+	 * @return the help message
+	 */
+	public String produceHelpMessage() {
+		// first calculate how much space is necessary for the options
+		int maxlen = 0;
+		for (OptionContainer oc : options) {
+			String shorta = oc.getShort();
+			String longa = oc.getLong();
+			int len = 0;
+			if (shorta != null) len += shorta.length() + 1 + 1;
+			if (longa != null) len += longa.length() + 1 + (longa.charAt(0) == 'X' ? 0 : 1) + 1;
+			// yes, we don't care about branch mispredictions here ;)
+			if (maxlen < len) maxlen = len;
+		}
+		
+		// get the individual strings
+		StringBuilder ret = new StringBuilder();
+		for (OptionContainer oc : options) {
+			ret.append(produceHelpMessage(oc, maxlen));
+		}
+
+		return ret.toString();
+	}
+
+	/**
+	 * Returns the help message for the given OptionContainer.  The
+	 * command line flags will be padded to optwidth spaces to allow for
+	 * aligning.  The description of the flags will be wrapped at 80
+	 * characters.
+	 *
+	 * @param oc the OptionContainer
+	 * @param indentwidth padding width for the command line flags
+	 * @return the help message for the option
+	 */
+	public String produceHelpMessage(OptionContainer oc, int indentwidth) {
+		String desc = oc.getDescription();
+		if (desc == null) return "";
+
+		String shorta = oc.getShort();
+		String longa = oc.getLong();
+		int optwidth = 0;
+		if (shorta != null) optwidth += shorta.length() + 1 + 1;
+		if (longa != null) optwidth += longa.length() + 1 + (longa.charAt(0) == 'X' ? 0 : 1) + 1;
+		int descwidth = 80 - indentwidth;
+
+		// default to with of command line flags if no width given
+		if (indentwidth <= 0) indentwidth = optwidth;
+
+		StringBuilder ret = new StringBuilder();
+
+		// add the command line flags
+		if (shorta != null) ret.append('-').append(shorta).append(' ');
+		if (longa != null) {
+			ret.append('-');
+			if (longa.charAt(0) != 'X') ret.append('-');
+			ret.append(longa).append(' ');
+		}
+
+		for (int i = optwidth; i < indentwidth; i++) ret.append(' ');
+		// add the description, wrap around words
+		int pos = 0, lastpos = 0;
+		while (pos < desc.length()) {
+			pos += descwidth;
+			if (lastpos != 0) {
+				for (int i = 0; i < indentwidth; i++) ret.append(' ');
+			}
+			if (pos >= desc.length()) {
+				ret.append(desc.substring(lastpos)).append('\n');
+				break;
+			}
+			int space;
+			for (space = pos; desc.charAt(space) != ' '; space--) {
+				if (space == 0) {
+					space = pos;
+					break;
+				}
+			}
+			pos = space;
+			ret.append(desc.substring(lastpos, pos)).append('\n');
+			while (desc.charAt(pos) == ' ') pos++;
+			lastpos = pos;
+		}
+
+		return ret.toString();
+	}
+
+	public class OptionContainer {
+		int cardinality;
+		String shorta;
+		String longa;
+		List<String> values = new ArrayList<String>();
+		String name;
+		String defaulta;
+		String descriptiona;
+		boolean present;
+
+		public OptionContainer(
+				String shorta,
+				String longa,
+				int cardinality,
+				String defaulta,
+				String descriptiona)
+			throws IllegalArgumentException
+		{
+			this.cardinality = cardinality;
+			this.shorta = shorta;
+			this.longa = longa;
+			this.defaulta = defaulta;
+			this.descriptiona = descriptiona;
+			this.present = false;
+
+			if (cardinality != CAR_ZERO &&
+					cardinality != CAR_ONE &&
+					cardinality != CAR_ZERO_ONE &&
+					cardinality != CAR_ZERO_MANY &&
+					cardinality != CAR_ONE_MANY)
+				throw new IllegalArgumentException("unknown cardinality");
+			if (shorta != null && shorta.length() != 1) throw
+				new IllegalArgumentException("short option should consist of exactly one character");
+			if (shorta == null && longa == null) throw
+				new IllegalArgumentException("either a short or long argument should be given");
+			if ((cardinality == CAR_ZERO ||
+					cardinality == CAR_ZERO_ONE ||
+					cardinality == CAR_ZERO_MANY) &&
+					defaulta != null) {
+				throw new IllegalArgumentException("cannot specify a default for a (possible) zero argument option");
+			}
+
+			name = (longa != null) ? longa : shorta;
+			
+			options.add(this);
+		}
+
+		public void resetArguments() {
+			values.clear();
+		}
+
+		public void addArgument(String val) throws OptionsException {
+			if (cardinality == CAR_ZERO) {
+				throw new OptionsException("option " + name + " does not allow arguments");
+			} else if ((cardinality == CAR_ONE ||
+					cardinality == CAR_ZERO_ONE) &&
+					values.size() >= 1) {
+				throw new OptionsException("option " + name + " does at max allow only one argument");
+			}
+			// we can add it
+			values.add(val);
+			setPresent();
+		}
+
+		public void setPresent() {
+			present = true;
+		}
+
+		public boolean isPresent() {
+			return present;
+		}
+
+		public int getCardinality() {
+			return cardinality;
+		}
+
+		public int getArgumentCount() {
+			return values.size();
+		}
+
+		public String getArgument() {
+			String ret = getArgument(1);
+			if (ret == null) ret = defaulta;
+			return ret;
+		}
+
+		public String getArgument(int index) {
+			String[] args = getArguments();
+			if (index < 1 || index > args.length) return null;
+			return args[index - 1];
+		}
+
+		public String[] getArguments() {
+			return values.toArray(new String[values.size()]);
+		}
+
+		public String getShort() {
+			return shorta;
+		}
+
+		public String getLong() {
+			return longa;
+		}
+
+		public String getDescription() {
+			return descriptiona;
+		}
+	}
+}