ControlledString.java

package bjc.everge;

/**
 * Represents a string with a set of control flags attached to it.
 *
 * @author Ben Culkin
 */
public class ControlledString {
	/**
	 * Represents a single control (a key-values pair)
	 *
	 * @author Ben Culkin
	 */
	public static class Control {
		/**
		 * The name of the control.
		 */
		public String name;

		/**
		 * The arguments to the control.
		 */
		public String[] args;

		/**
		 * Create a new blank control.
		 */
		public Control() {

		}

		/**
		 * Create a new argless control.
		 *
		 * @param nam
		 * 	The name of the control.
		 */
		public Control(String nam) {
			name = nam;
		}

		/**
		 * Create a new control.
		 *
		 * @param nam
		 * 	The name of the control.
		 * @param ars
		 * 	The arguments of the control.
		 */
		public Control(String nam, String... ars) {
			name = nam;
			args = ars;
		}
	}

	/**
	 * The string the controls apply to.
	 */
	public String strang;

	/**
	 * The controls that apply to the string.
	 */
	public Control[] controls;

	/**
	 * Create a new blank controlled string.
	 */
	public ControlledString() {
		controls = new Control[0];
	}

	/**
	 * Create a new controlled string without any controls.
	 *
	 * @param strung
	 * 	The string to use.
	 */
	public ControlledString(String strung) {
		strang = strung;

		controls = new Control[0];
	}

	/**
	 * Create a new controlled string.
	 *
	 * @param strung
	 * 	The string to use.
	 * @param controls
	 * 	The controls that apply to the string.
	 */
	public ControlledString(String strung, Control... controls) {
		strang = strung;

		controls = controls;
	}

	/**
	 * Check if the string has controls.
	 *
	 * @return Whether or not the string has controls.
	 */
	public boolean hasControls() {
		return controls.length > 0;
	}

	/**
	 * Parse a controlled string from a regular string.
	 *
	 * The controls must be parsed from the beginning of the string, and are indicated by occurances
	 * of contInd that bracket them from the string. The individual controls are delimited by
	 * instances of contSep, with arguments to them being separated by occurances of contArg.
	 *
	 * Each of those separators (which must be regular strings, not regexes or anything) may be
	 * escaped by preceeding them with a copy of contEsc.
	 *
	 * @param lne
	 * 	The string to parse frmo.
	 * @param contInd
	 * 	The indicator for whether or not there are controls.
	 * @param contSep
	 * 	The separator of individual controls.
	 * @param contArg
	 * 	The separator of control arguments.
	 * @param contEsc
	 * 	The escape string for each of the separators/indicators.
	 *
	 * @return A parsed control string.
	 */
	public static ControlledString parse(String lne, String contInd, String contSep,
			String contArg, String contEsc) {
		if (!lne.startsWith(contInd)) {
			return new ControlledString(lne);
		}

		String tmp = lne.substring(2);

		String[] bits = StringUtils.escapeSplit(contEsc, contInd, lne);

		if (bits.length < 2) {
			String msg = "Did not find control terminator (%s) where it should be";
			msg = String.format(msg, contInd);

			throw new IllegalArgumentException(msg);
		}

		ControlledString cs = new ControlledString(bits[0]);

		bits = StringUtils.escapeSplit(contEsc, contSep, bits[1]);

		cs.controls = new Control[bits.length];

		for (int i = 0; i < bits.length; i++) {
			String bit = bits[i];

			String[] bots = StringUtils.escapeSplit(contEsc, contArg, bit);

			Control cont = new Control(bots[0]);

			if (cont.name.length() > 1) {
				cont.name = cont.name.toUpperCase();
			}

			if (bots.length > 1) {
				cont.args = new String[bots.length - 1];
				for (int j = 1; j < bots.length; j++) {
					cont.args[j - 1] = bots[j];
				}
			}

			cs.controls[i] = cont;
		}

		return cs;
	}
}