package bjc.rgens.newparser;
import static bjc.rgens.newparser.CaseElement.ElementType.*;
/*
* @TODO 10/11/17 Ben Culkin :CaseElementSplit
* Split this into multiple subclasses based off of a value of ElementType.
*/
/**
* A element in a rule case.
*
* @author EVE
*/
public class CaseElement {
/**
* The possible types of an element.
*
* @author EVE
*/
public static enum ElementType {
/** An element that represents a literal string. */
LITERAL,
/** An element that represents a rule reference. */
RULEREF,
/** An element that represents a random range. */
RANGE,
/** An element that represents a variable that stores a string. */
VARDEF,
/**
* An element that represents a variable that stores the result
* of generating a rule.
*/
EXPVARDEF;
}
/* Regexps for marking rule types. */
private static final String SPECIAL_CASELEM = "\\{[^}]+\\}";
private static final String REFER_CASELEM = "\\[[^\\]]+\\]";
private static final String RANGE_CASELM = "\\[\\d+\\.\\.\\d+\\]";
/** The type of this element. */
public final ElementType type;
/**
* The literal string value of this element.
*
* This means that it is a string whose value should always mean the
* same thing.
*
*
Used For
*
* - LITERAL
* - The string this element represents
* - RULEREF
* - The name of the rule this element references
*
*/
private String literalVal;
/**
* The starting integer value of this element.
*
* Used For
*
* - RANGE
* - The inclusive start of the range
*
*/
private int start;
/**
* The starting integer value of this element.
*
* Used For
*
* - RANGE
* - The inclusive end of the range
*
*/
private int end;
/**
* The name of the variable this element defines.
*
* Used For
*
* - VARDEF
* - The name of the variable
* - EXPVARDEF
* - The name of the variable
*
*/
private String varName;
/**
* The definition of the variable this element defines.
*
* Used For
*
* - VARDEF
* - The value of the variable
* - EXPVARDEF
* - The rule to expand for the value of this variable
*
*/
private String varDef;
/**
* Create a new case element.
*
* @param typ
* The type of this element.
*
* @throws IllegalArgumentException
* If the specified type needs parameters.
*/
public CaseElement(ElementType typ) {
switch (typ) {
case LITERAL:
case RULEREF:
throw new IllegalArgumentException("This type requires a string parameter");
case RANGE:
throw new IllegalArgumentException("This type requires two integer parameters");
case VARDEF:
case EXPVARDEF:
throw new IllegalArgumentException("This type requires two string parameters");
default:
break;
}
type = typ;
}
/**
* Create a new case element that has a single string value.
*
* @param typ
* The type of this element.
*
* @param val
* The string value of this element.
*
* @throws IllegalArgumentException
* If the specified type doesn't take a single string parameter.
*/
public CaseElement(ElementType typ, String val) {
switch (typ) {
case LITERAL:
case RULEREF:
break;
case RANGE:
throw new IllegalArgumentException("This type requires two integer parameters");
case VARDEF:
case EXPVARDEF:
throw new IllegalArgumentException("This type requires two string parameters");
default:
throw new IllegalArgumentException("This type doesn't have a string parameter");
}
type = typ;
literalVal = val;
}
/**
* Create a new case element that has two integer values.
*
* @param typ
* The type of this element.
*
* @param first
* The first integer value for this element.
*
* @param second
* The second integer value for this element.
*
* @throws IllegalArgumentException
* If the specified type doesn't take two integer parameters.
*/
public CaseElement(ElementType typ, int first, int second) {
switch (typ) {
case LITERAL:
case RULEREF:
throw new IllegalArgumentException("This type requires a string parameter");
case RANGE:
break;
case VARDEF:
case EXPVARDEF:
throw new IllegalArgumentException("This type requires two string parameters");
default:
throw new IllegalArgumentException("This type doesn't have two integer parameters");
}
type = typ;
this.start = first;
this.end = second;
}
/**
* Create a new case element that has two string values.
*
* @param typ
* The type of this element.
*
* @param name
* The first string value for this element.
*
* @param def
* The second string value for this element.
*
* @throws IllegalArgumentException
* If the specified type doesn't take two string parameters.
*/
public CaseElement(ElementType typ, String name, String def) {
switch (typ) {
case LITERAL:
case RULEREF:
throw new IllegalArgumentException("This type requires a string parameter");
case RANGE:
throw new IllegalArgumentException("This type requires two integer parameters");
case VARDEF:
case EXPVARDEF:
break;
default:
throw new IllegalArgumentException("This type doesn't have two string parameters");
}
type = typ;
this.varName = name;
this.varDef = def;
}
/**
* Get the literal string value for this element.
*
* @return
* The literal string value for this element.
*
* @throws IllegalStateException
* If this type doesn't have a literal string value.
*/
public String getLiteral() {
switch (type) {
case LITERAL:
case RULEREF:
break;
default:
throw new IllegalStateException(
String.format("Type '%s' doesn't have a literal string value"));
}
return literalVal;
}
/**
* Get the starting integer value for this element.
*
* @return
* The starting integer value for this element.
*
* @throws IllegalStateException
* If this type doesn't have a starting integer value.
*/
public int getStart() {
switch (type) {
case RANGE:
break;
default:
throw new IllegalStateException(
String.format("Type '%s' doesn't have a starting integer value", type));
}
return start;
}
/**
* Get the ending integer value for this element.
*
* @return
* The ending integer value for this element.
*
* @throws IllegalStateException
* If this type doesn't have a ending integer value.
*/
public int getEnd() {
switch (type) {
case RANGE:
break;
default:
throw new IllegalStateException(
String.format("Type '%s' doesn't have a ending integer value", type));
}
return end;
}
/**
* Get the variable name for this element.
*
* @return
* The variable name of this element.
*
* @throws IllegalStateException
* If the type doesn't have a variable name.
*/
public String getName() {
switch (type) {
case VARDEF:
case EXPVARDEF:
break;
default:
throw new IllegalStateException(String.format("Type '%s' doesn't have a name", type));
}
return varName;
}
/**
* Get the variable definition for this element.
*
* @return
* The variable definition of this element.
*
* @throws IllegalStateException
* If the type doesn't have a variable definition.
*/
public String getDefn() {
switch (type) {
case VARDEF:
case EXPVARDEF:
break;
default:
throw new IllegalStateException(String.format("Type '%s' doesn't have a name", type));
}
return varDef;
}
@Override
public String toString() {
switch (type) {
case LITERAL:
case RULEREF:
return literalVal;
case RANGE:
return String.format("[%d..%d]", start, end);
case VARDEF:
return String.format("{%s:=%s}", varName, varDef);
case EXPVARDEF:
return String.format("{%s=%s}", varName, varDef);
default:
return String.format("Unknown type '%s'", type);
}
}
/**
* Create a case element from a string.
*
* @param csepart
* The string to convert.
*
* @return
* A case element representing the string.
*/
public static CaseElement createElement(String csepart) {
if (csepart == null) {
throw new NullPointerException("Case part cannot be null");
}
if (csepart.matches(SPECIAL_CASELEM)) {
/* Handle special cases. */
String specialBody = csepart.substring(1, csepart.length() - 1);
if (specialBody.matches("\\S+:=\\S+")) {
/* Handle expanding variable definitions. */
String[] parts = specialBody.split(":=");
if (parts.length != 2) {
String msg = "Expanded variables must be a name and a definition, seperated by :=";
throw new GrammarException(msg);
}
return new CaseElement(EXPVARDEF, parts[0], parts[1]);
} else if (specialBody.matches("\\S+=\\S+")) {
/* Handle regular variable definitions. */
String[] parts = specialBody.split("=");
if (parts.length != 2) {
String msg = "Variables must be a name and a definition, seperated by =";
throw new GrammarException(msg);
}
return new CaseElement(VARDEF, parts[0], parts[1]);
} else if (specialBody.matches("{empty}")) {
/* Literal blank, for empty cases. */
return new CaseElement(LITERAL, "");
} else {
throw new IllegalArgumentException(
String.format("Unknown special case part '%s'", specialBody));
}
} else if (csepart.matches(REFER_CASELEM)) {
if (csepart.matches(RANGE_CASELM)) {
/* Handle ranges */
String rawRange = csepart.substring(1, csepart.length() - 1);
int firstNum = Integer.parseInt(rawRange.substring(0, rawRange.indexOf('.')));
int secondNum = Integer.parseInt(rawRange.substring(rawRange.lastIndexOf('.') + 1));
return new CaseElement(RANGE, firstNum, secondNum);
}
return new CaseElement(RULEREF, csepart);
} else {
return new CaseElement(LITERAL, csepart);
}
}
}