ControlledString.java
package bjc.everge;
import java.util.Arrays;
/**
* 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;
}
/**
* Get the count of arguments this control has.
*
* @return The number of arguments to this control.
*/
public int count() {
if (args == null) return 0;
return args.length;
}
/**
* Get an argument from the control.
*
* @param i
* The index of the argument to get.
* @return The argument at that position.
*/
public String get(int i) {
if (i < 0) {
String msg = String.format(
"Control argument index must be greater than 0 (was %d)", i);
throw new IndexOutOfBoundsException(msg);
}
if (i > args.length) {
String msg = String.format(
"Control argument index must be less than %d (was %d)",
args.length, i);
throw new IndexOutOfBoundsException(msg);
}
return args[i];
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name);
if (args != null && args.length > 0) {
sb.append("/");
for (String arg : args) {
sb.append(arg);
sb.append(";");
}
}
return sb.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(args);
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Control other = (Control) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
boolean isArged = args != null && args.length > 0;
boolean oIsArged = other.args != null && other.args.length > 0;
if (isArged && !oIsArged) {
return false;
}
if (!isArged && oIsArged) {
return false;
}
if (isArged && oIsArged) {
return Arrays.equals(args, other.args);
}
return true;
}
/**
* Convenient static constructor for static imports.
*
* @param nam
* The name of the control.
* @param ars
* The arguments to the control.
* @return A control with the right parameters.
*/
public static Control C(String nam, String... ars) {
return new Control(nam, ars);
}
}
/**
* Parameter class for defining how to parse a ControlledString.
*
* @author Ben Culkin
*/
public static class ParseStrings {
/**
* The indicator for separating controls from the regular string.
*/
public String contInd;
/**
* The indicator for separating individual controls.
*/
public String contSep;
/**
* The indicator for separating arguments to a control.
*/
public String contArg;
/**
* The indicator for escaping any of the indicators (including itself)
*/
public String contEsc;
/**
* Create a new set of parse strings.
*
* @param contInd
* The control indicator.
* @param contSep
* The control separator.
* @param contArg
* The argument separator.
* @param contEsc
* The control escape.
*/
public ParseStrings(String contInd, String contSep, String contArg,
String contEsc) {
this.contInd = contInd;
this.contSep = contSep;
this.contArg = contArg;
this.contEsc = contEsc;
}
/**
* Convenient static constructor.
*
* @param contInd
* The control indicator.
* @param contSep
* The control separator.
* @param contArg
* The argument separator.
* @param contEsc
* The control escape.
* @return A new set of control strings.
*/
public static ParseStrings PS(String contInd, String contSep, String contArg,
String contEsc) {
return new ParseStrings(contInd, contSep, contArg, contEsc);
}
}
/**
* 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;
this.controls = controls;
}
/**
* Check if the string has controls.
*
* @return Whether or not the string has controls.
*/
public boolean hasControls() {
return controls.length > 0;
}
/**
* Get the count of controls.
*
* @return The number of controls for this string.
*/
public int count() {
return controls.length;
}
/**
* Parse a controlled string from a regular string.
*
* The controls must be parsed from the beginning of the string.
*
* @param lne
* The string to parse from.
* @param strangs
* The object to read the strings from
* @return A parsed control string.
*/
public static ControlledString parse(String lne, ParseStrings strangs) {
if (!lne.startsWith(strangs.contInd)) {
return new ControlledString(lne);
}
String[] bits = StringUtils.escapeSplit(strangs.contEsc, strangs.contInd, lne);
if (bits.length < 2) {
String msg = "Did not find control terminator (%s) where it should be";
msg = String.format(msg, strangs.contInd);
throw new IllegalArgumentException(msg);
}
ControlledString cs = new ControlledString(bits[0]);
if (bits.length > 2)
cs.strang = bits[2];
bits = StringUtils.escapeSplit(strangs.contEsc, strangs.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(strangs.contEsc, strangs.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;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("//");
for (Control cont : controls) {
sb.append(cont);
}
sb.append("//");
sb.append(strang);
return sb.toString();
}
}