diff options
15 files changed, 812 insertions, 593 deletions
diff --git a/base/src/main/java/bjc/utils/ioutils/format/AestheticDirective.java b/base/src/main/java/bjc/utils/ioutils/format/AestheticDirective.java index 869834b..9cc10d2 100644 --- a/base/src/main/java/bjc/utils/ioutils/format/AestheticDirective.java +++ b/base/src/main/java/bjc/utils/ioutils/format/AestheticDirective.java @@ -1,61 +1,61 @@ -package bjc.utils.ioutils.format;
-
-import java.util.regex.Matcher;
-
-import bjc.utils.esodata.Tape;
-
-public class AestheticDirective implements Directive {
-
- @Override
- public void format(StringBuffer sb, Object item, CLModifiers mods, CLParameters params, Tape<Object> tParams,
- Matcher dirMatcher) {
- CLFormatter.checkItem(item, 'A');
-
- int mincol = 0, colinc = 1, minpad = 0;
- char padchar = ' ';
-
- if (params.length() > 1) {
- mincol = params.getIntDefault(0, "minimum column count", 'A', 0);
- }
-
- if (params.length() < 4) {
- throw new IllegalArgumentException(
- "Must provide either zero, one or four arguments to A directive");
- }
-
- colinc = params.getIntDefault(1, "padding increment", 'A', 1);
- minpad = params.getIntDefault(2, "minimum amount of padding", 'A', 0);
- padchar = params.getCharDefault(3, "padding character", 'A', ' ');
-
- StringBuilder work = new StringBuilder();
-
- if (mods.atMod) {
- for (int i = 0; i < minpad; i++) {
- work.append(padchar);
- }
-
- for (int i = work.length(); i < mincol; i++) {
- for (int k = 0; k < colinc; k++) {
- work.append(padchar);
- }
- }
- }
-
- work.append(item.toString());
-
- if (!mods.atMod) {
- for (int i = 0; i < minpad; i++) {
- work.append(padchar);
- }
-
- for (int i = work.length(); i < mincol; i++) {
- for (int k = 0; k < colinc; k++) {
- work.append(padchar);
- }
- }
- }
-
- tParams.right();
- }
-
-}
+package bjc.utils.ioutils.format; + +import java.util.regex.Matcher; + +import bjc.utils.esodata.Tape; + +class AestheticDirective implements Directive { + + @Override + public void format(StringBuffer sb, Object item, CLModifiers mods, CLParameters params, Tape<Object> tParams, + Matcher dirMatcher, CLFormatter fmt) { + CLFormatter.checkItem(item, 'A'); + + int mincol = 0, colinc = 1, minpad = 0; + char padchar = ' '; + + if (params.length() > 1) { + mincol = params.getIntDefault(0, "minimum column count", 'A', 0); + } + + if (params.length() < 4) { + throw new IllegalArgumentException( + "Must provide either zero, one or four arguments to A directive"); + } + + colinc = params.getIntDefault(1, "padding increment", 'A', 1); + minpad = params.getIntDefault(2, "minimum amount of padding", 'A', 0); + padchar = params.getCharDefault(3, "padding character", 'A', ' '); + + StringBuilder work = new StringBuilder(); + + if (mods.atMod) { + for (int i = 0; i < minpad; i++) { + work.append(padchar); + } + + for (int i = work.length(); i < mincol; i++) { + for (int k = 0; k < colinc; k++) { + work.append(padchar); + } + } + } + + work.append(item.toString()); + + if (!mods.atMod) { + for (int i = 0; i < minpad; i++) { + work.append(padchar); + } + + for (int i = work.length(); i < mincol; i++) { + for (int k = 0; k < colinc; k++) { + work.append(padchar); + } + } + } + + tParams.right(); + } + +} diff --git a/base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java b/base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java index 8abc0ff..408bb3b 100644 --- a/base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java +++ b/base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java @@ -1,456 +1,171 @@ -package bjc.utils.ioutils.format;
-
-import java.util.HashMap;
-import java.util.IllegalFormatConversionException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.UnknownFormatConversionException;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import bjc.utils.esodata.Tape;
-import bjc.utils.ioutils.NumberUtils;
-import bjc.utils.esodata.SingleTape;
-
-import static bjc.utils.PropertyDB.applyFormat;
-
-import static bjc.utils.PropertyDB.getRegex;
-
-public class CLFormatter {
- private static final String prefixParam = getRegex("clFormatPrefix");
- private static final Pattern pPrefixParam = Pattern.compile(prefixParam);
-
- private static final String formatMod = getRegex("clFormatModifier");
-
- private static final String prefixList = applyFormat("delimSeparatedList", prefixParam, ",");
-
- private static final String directiveName = getRegex("clFormatName");
-
- private static final String formatDirective = applyFormat("clFormatDirective", prefixList, formatMod,
- directiveName);
- private static final Pattern pFormatDirective = Pattern.compile(formatDirective);
-
- private static Map<String, Directive> builtinDirectives;
- private Map<String, Directive> extraDirectives;
-
- static {
- builtinDirectives = new HashMap<>();
-
- builtinDirectives.put("A", new AestheticDirective());
- }
-
- public CLFormatter() {
- extraDirectives = new HashMap<>();
- }
-
- public static void checkItem(Object itm, char directive) {
- if (itm == null)
- throw new IllegalArgumentException(String.format("No argument provided for %c directive", directive));
- }
-
- public String formatString(String format, Object... params) {
- StringBuffer sb = new StringBuffer();
- /* Put the parameters where we can easily handle them. */
- Tape<Object> tParams = new SingleTape<>(params);
-
- doFormatString(format, sb, tParams);
-
- return sb.toString();
- }
-
- private void doFormatString(String format, StringBuffer sb, Tape<Object> tParams) {
- Matcher dirMatcher = pFormatDirective.matcher(format);
-
- while (dirMatcher.find()) {
- dirMatcher.appendReplacement(sb, "");
-
- String dirName = dirMatcher.group("name");
- String dirFunc = dirMatcher.group("funcname");
- String dirMods = dirMatcher.group("modifiers");
- String dirParams = dirMatcher.group("params");
-
- CLParameters arrParams = CLParameters.fromDirective(dirParams.split("(?<!'),"), tParams);
- CLModifiers mods = CLModifiers.fromString(dirMods);
-
- Object item = tParams.item();
- if (dirName == null && dirFunc != null) {
- /*
- * @TODO implement user-called functions.
- */
- continue;
- }
-
- if (builtinDirectives.containsKey(dirName)) {
- builtinDirectives.get(dirName).format(sb, item, mods, arrParams, tParams, dirMatcher);
- }
-
- switch (dirName) {
- case "B":
- checkItem(item, 'B');
- if (!(item instanceof Number)) {
- throw new IllegalFormatConversionException('B', item.getClass());
- }
- handleNumberDirective(sb, mods, arrParams, -1, ((Number) item).longValue(), 2);
- tParams.right();
- break;
- case "C":
- checkItem(item, 'C');
- handleCDirective(sb, item, mods);
- tParams.right();
- break;
- case "D":
- checkItem(item, 'D');
- if (!(item instanceof Number)) {
- throw new IllegalFormatConversionException('D', item.getClass());
- }
- handleNumberDirective(sb, mods, arrParams, -1, ((Number) item).longValue(), 10);
- tParams.right();
- break;
- case "O":
- checkItem(item, 'O');
- if (!(item instanceof Number)) {
- throw new IllegalFormatConversionException('O', item.getClass());
- }
- handleNumberDirective(sb, mods, arrParams, -1, ((Number) item).longValue(), 8);
- tParams.right();
- break;
- case "R":
- checkItem(item, 'R');
- handleRadixDirective(sb, mods, arrParams, item);
- tParams.right();
- break;
- case "X":
- checkItem(item, 'X');
- if (!(item instanceof Number)) {
- throw new IllegalFormatConversionException('X', item.getClass());
- }
- handleNumberDirective(sb, mods, arrParams, -1, ((Number) item).longValue(), 16);
- tParams.right();
- break;
- case "&":
- handleFreshlineDirective(sb, arrParams);
- break;
- case "%":
- handleLiteralDirective(sb, arrParams, "\n", '%');
- break;
- case "|":
- handleLiteralDirective(sb, arrParams, "\f", '|');
- break;
- case "~":
- handleLiteralDirective(sb, arrParams, "~", '~');
- break;
- case "*":
- handleGotoDirective(mods, arrParams, tParams);
- break;
- case "^":
- handleEscapeDirective(mods, arrParams, tParams);
- break;
- case "[":
- handleConditionalDirective(sb, mods, arrParams, tParams, dirMatcher);
- break;
- case "]":
- throw new IllegalArgumentException("Found conditional-end outside of conditional.");
- case ";":
- throw new IllegalArgumentException("Found conditional-seperator outside of conditional.");
- case "T":
- case "<":
- case ">":
- /*
- * @TODO Figure out how to implement tabulation/justification in a reasonable
- * manner.
- */
- throw new IllegalArgumentException("Layout-control directives aren't implemented yet.");
- case "F":
- case "E":
- case "G":
- case "$":
- /* @TODO implement floating point directives. */
- throw new IllegalArgumentException("Floating-point directives aren't implemented yet.");
- case "S":
- case "W":
- /*
- * @TODO figure out if we want to implement someting for these directives
- * instead of punting.
- */
- throw new IllegalArgumentException("S and W aren't implemented. Use A instead");
- default:
- String msg = String.format("Unknown format directive '%s'", dirName);
- throw new UnknownFormatConversionException(msg);
- }
- }
-
- dirMatcher.appendTail(sb);
- }
-
- private void handleCDirective(StringBuffer buff, Object parm, CLModifiers mods) {
- if (!(parm instanceof Character)) {
- throw new IllegalFormatConversionException('C', parm.getClass());
- }
-
- char ch = (Character) parm;
- int codepoint = (int) ch;
-
- if (mods.colonMod) {
- /*
- * Colon mod means print Unicode character name.
- */
- buff.append(Character.getName(codepoint));
- } else {
- buff.append(ch);
- }
- }
-
- private void handleFreshlineDirective(StringBuffer buff, CLParameters params) {
- int nTimes = 1;
-
- if (params.length() > 1) {
- nTimes = params.getInt(0, "occurance count", '&');
- }
-
- if (buff.charAt(buff.length() - 1) == '\n')
- nTimes -= 1;
-
- for (int i = 0; i < nTimes; i++) {
- buff.append("\n");
- }
- }
-
- private void handleLiteralDirective(StringBuffer buff, CLParameters params, String lit, char directive) {
- int nTimes = 1;
-
- if (params.length() > 1) {
- nTimes = params.getInt(0, "occurance count", directive);
- }
-
- for (int i = 0; i < nTimes; i++) {
- buff.append(lit);
- }
- }
-
- private void handleNumberDirective(StringBuffer buff, CLModifiers mods, CLParameters params, int argidx, long val,
- int radix) {
- /*
- * Initialize the two padding related parameters, and then fill them in from the
- * directive parameters if they are present.
- */
- int mincol = 0;
- char padchar = ' ';
- if (params.length() > (argidx + 2)) {
- mincol = params.getIntDefault(argidx + 1, "minimum column count", 'R', 0);
- }
- if (params.length() > (argidx + 3)) {
- padchar = params.getCharDefault(argidx + 2, "padding character", 'R', ' ');
- }
-
- if (mods.colonMod) {
- /*
- * We're doing commas, so check if the two comma-related parameters were
- * supplied.
- */
- int commaInterval = 0;
- char commaChar = ',';
- if (params.length() > (argidx + 3)) {
- commaChar = params.getCharDefault((argidx + 3), "comma character", 'R', ' ');
- }
- if (params.length() > (argidx + 4)) {
- commaInterval = params.getIntDefault((argidx + 4), "comma interval", 'R', 0);
- }
-
- NumberUtils.toCommaString(val, mincol, padchar, commaInterval, commaChar, mods.atMod, radix);
- } else {
- NumberUtils.toNormalString(val, mincol, padchar, mods.atMod, radix);
- }
- }
-
- private void handleRadixDirective(StringBuffer buff, CLModifiers mods, CLParameters params, Object arg) {
- if (!(arg instanceof Number)) {
- throw new IllegalFormatConversionException('R', arg.getClass());
- }
-
- /*
- * @TODO see if this is the way we want to do this.
- */
- long val = ((Number) arg).longValue();
-
- if (params.length() == 0) {
- if (mods.atMod) {
- buff.append(NumberUtils.toRoman((Long) val, mods.colonMod));
- } else if (mods.colonMod) {
- buff.append(NumberUtils.toOrdinal(val));
- } else {
- buff.append(NumberUtils.toCardinal(val));
- }
- } else {
- if (params.length() < 1)
- throw new IllegalArgumentException("R directive requires at least one parameter, the radix");
-
- int radix = params.getInt(0, "radix", 'R');
-
- handleNumberDirective(buff, mods, params, 0, val, radix);
- }
- }
-
- private void handleGotoDirective(CLModifiers mods, CLParameters params, Tape<Object> formatParams) {
- if (mods.colonMod) {
- int num = 1;
- if (params.length() > 1) {
- num = params.getIntDefault(0, "number of arguments backward", '*', 1);
- }
-
- formatParams.left(num);
- } else if (mods.atMod) {
- int num = 0;
- if (params.length() > 1) {
- num = params.getIntDefault(0, "argument index", '*', 0);
- }
-
- formatParams.first();
- formatParams.right(num);
- } else {
- int num = 1;
- if (params.length() > 1) {
- num = params.getIntDefault(0, "number of arguments forward", '*', 1);
- }
-
- formatParams.right(num);
- }
- }
-
- private void handleConditionalDirective(StringBuffer sb, CLModifiers mods, CLParameters arrParams,
- Tape<Object> formatParams, Matcher dirMatcher) {
- StringBuffer condBody = new StringBuffer();
-
- List<String> clauses = new ArrayList<>();
- String defClause = null;
- boolean isDefault = false;
-
- while (dirMatcher.find()) {
- /* Process a list of clauses. */
- String dirName = dirMatcher.group("name");
- String dirMods = dirMatcher.group("modifiers");
-
- if (dirName != null) {
- /* Append everything up to this directive. */
- dirMatcher.appendReplacement(condBody, "");
-
- if (dirName.equals("]")) {
- /* End the conditional. */
- String clause = condBody.toString();
- if (isDefault) {
- defClause = clause;
- } else {
- clauses.add(clause);
- }
-
- break;
- } else if (dirName.equals(";")) {
- /* End the clause. */
- String clause = condBody.toString();
- if (isDefault) {
- defClause = clause;
- } else {
- clauses.add(clause);
- }
-
- /* Mark the next clause as the default. */
- if (dirMods.contains(":")) {
- isDefault = true;
- }
- } else {
- /* Not a special directive. */
- condBody.append(dirMatcher.group());
- }
- }
- }
-
- Object par = formatParams.item();
- if (mods.colonMod) {
- formatParams.right();
-
- if (par == null) {
- throw new IllegalArgumentException("No parameter provided for [ directive.");
- } else if (!(par instanceof Boolean)) {
- throw new IllegalFormatConversionException('[', par.getClass());
- }
- boolean res = (Boolean) par;
-
- String fmt;
- if (res)
- fmt = clauses.get(1);
- else
- fmt = clauses.get(0);
-
- doFormatString(fmt, sb, formatParams);
- } else if (mods.atMod) {
- if (par == null) {
- throw new IllegalArgumentException("No parameter provided for [ directive.");
- } else if (!(par instanceof Boolean)) {
- throw new IllegalFormatConversionException('[', par.getClass());
- }
- boolean res = (Boolean) par;
-
- if (res) {
- doFormatString(clauses.get(0), sb, formatParams);
- } else {
- formatParams.right();
- }
- } else {
- int res;
- if (arrParams.length() > 1) {
- res = arrParams.getInt(0, "conditional choice", '[');
- } else {
- if (par == null) {
- throw new IllegalArgumentException("No parameter provided for [ directive.");
- } else if (!(par instanceof Number)) {
- throw new IllegalFormatConversionException('[', par.getClass());
- }
- res = ((Number) par).intValue();
-
- formatParams.right();
- }
-
- if (res < 0 || res > clauses.size()) {
- if (defClause != null)
- doFormatString(defClause, sb, formatParams);
- } else {
- doFormatString(clauses.get(res), sb, formatParams);
- }
- }
- return;
- }
-
- private void handleEscapeDirective(CLModifiers mods, CLParameters params, Tape<Object> formatParams) {
- boolean shouldExit;
-
- switch (params.length()) {
- case 0:
- shouldExit = formatParams.size() == 0;
- break;
- case 1:
- int num = params.getInt(0, "condition count", '^');
- shouldExit = num == 0;
- break;
- case 2:
- int left = params.getInt(0, "left-hand condition", '^');
- int right = params.getInt(1, "right-hand condition", '^');
- shouldExit = left == right;
- break;
- case 3:
- default:
- int low = params.getInt(0, "lower-bound condition", '^');
- int mid = params.getInt(1, "interval condition", '^');
- int high = params.getInt(2, "upper-bound condition", '^');
- shouldExit = (low <= mid) && (mid <= high);
- break;
- }
-
- /* At negates it. */
- if (mods.atMod)
- shouldExit = !shouldExit;
-
- if (shouldExit)
- throw new EscapeException(mods.colonMod);
- }
-
-}
+package bjc.utils.ioutils.format; + +import bjc.utils.esodata.SingleTape; +import bjc.utils.esodata.Tape; + +import java.util.HashMap; +import java.util.Map; +import java.util.UnknownFormatConversionException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static bjc.utils.PropertyDB.applyFormat; +import static bjc.utils.PropertyDB.getRegex; + +/** + * An implementation of CL's FORMAT. + * + * @author EVE + * + */ +public class CLFormatter { + private static final String prefixParam = getRegex("clFormatPrefix"); + + private static final String formatMod = getRegex("clFormatModifier"); + + private static final String prefixList = applyFormat("delimSeparatedList", prefixParam, ","); + + private static final String directiveName = getRegex("clFormatName"); + + private static final String formatDirective = applyFormat("clFormatDirective", prefixList, + formatMod, directiveName); + private static final Pattern pFormatDirective = Pattern.compile(formatDirective); + + private static Map<String, Directive> builtinDirectives; + + private Map<String, Directive> extraDirectives; + + static { + builtinDirectives = new HashMap<>(); + + builtinDirectives.put("A", new AestheticDirective()); + + builtinDirectives.put("C", new CharacterDirective()); + + builtinDirectives.put("B", new NumberDirective(-1, 2)); + builtinDirectives.put("O", new NumberDirective(-1, 8)); + builtinDirectives.put("D", new NumberDirective(-1, 10)); + builtinDirectives.put("X", new NumberDirective(-1, 16)); + + builtinDirectives.put("R", new RadixDirective()); + + builtinDirectives.put("&", new FreshlineDirective()); + + builtinDirectives.put("%", new LiteralDirective("\n", '%')); + builtinDirectives.put("|", new LiteralDirective("\f", '|')); + builtinDirectives.put("~", new LiteralDirective("~", '~')); + + builtinDirectives.put("*", new GotoDirective()); + + builtinDirectives.put("^", new EscapeDirective()); + builtinDirectives.put("[", new ConditionalDirective()); + } + + /** + * Create a new CL formatter. + */ + public CLFormatter() { + extraDirectives = new HashMap<>(); + } + + static void checkItem(Object itm, char directive) { + if(itm == null) throw new IllegalArgumentException( + String.format("No argument provided for %c directive", directive)); + } + + /** + * Format a string in the style of CL's FORMAT. + * + * @param format + * The format string to use. + * @param params + * The parameters for the string. + * @return The formatted string. + */ + public String formatString(String format, Object... params) { + StringBuffer sb = new StringBuffer(); + /* Put the parameters where we can easily handle them. */ + Tape<Object> tParams = new SingleTape<>(params); + + doFormatString(format, sb, tParams); + + return sb.toString(); + } + + void doFormatString(String format, StringBuffer sb, Tape<Object> tParams) { + Matcher dirMatcher = pFormatDirective.matcher(format); + + while(dirMatcher.find()) { + dirMatcher.appendReplacement(sb, ""); + + String dirName = dirMatcher.group("name"); + String dirFunc = dirMatcher.group("funcname"); + String dirMods = dirMatcher.group("modifiers"); + String dirParams = dirMatcher.group("params"); + + CLParameters arrParams = CLParameters.fromDirective(dirParams.split("(?<!'),"), tParams); + CLModifiers mods = CLModifiers.fromString(dirMods); + + Object item = tParams.item(); + if(dirName == null && dirFunc != null) { + /* + * @TODO implement user-called functions. + */ + continue; + } + + if(extraDirectives.containsKey(dirName)) { + extraDirectives.get(dirName).format(sb, item, mods, arrParams, tParams, dirMatcher, + this); + + continue; + } + + if(builtinDirectives.containsKey(dirName)) { + builtinDirectives.get(dirName).format(sb, item, mods, arrParams, tParams, dirMatcher, + this); + + continue; + } + + if(dirName == null) dirName = "<null>"; + + switch(dirName) { + case "]": + throw new IllegalArgumentException("Found conditional-end outside of conditional."); + case ";": + throw new IllegalArgumentException( + "Found conditional-seperator outside of conditional."); + case "T": + case "<": + case ">": + /* + * @TODO Figure out how to implement + * tabulation/justification in a reasonable + * manner. + */ + throw new IllegalArgumentException("Layout-control directives aren't implemented yet."); + case "F": + case "E": + case "G": + case "$": + /* @TODO implement floating point directives. */ + throw new IllegalArgumentException("Floating-point directives aren't implemented yet."); + case "S": + case "W": + /* + * @TODO figure out if we want to implement + * someting for these directives instead of + * punting. + */ + throw new IllegalArgumentException("S and W aren't implemented. Use A instead"); + default: + String msg = String.format("Unknown format directive '%s'", dirName); + throw new UnknownFormatConversionException(msg); + } + } + + dirMatcher.appendTail(sb); + } + +} diff --git a/base/src/main/java/bjc/utils/ioutils/format/CLParameters.java b/base/src/main/java/bjc/utils/ioutils/format/CLParameters.java index e3a03f6..5bdcbbf 100644 --- a/base/src/main/java/bjc/utils/ioutils/format/CLParameters.java +++ b/base/src/main/java/bjc/utils/ioutils/format/CLParameters.java @@ -13,10 +13,21 @@ import bjc.utils.esodata.Tape; public class CLParameters { private String[] params; + /** + * Create a new set of CL format parameters. + * + * @param params + * The CL format parameters to use. + */ public CLParameters(String[] params) { this.params = params; } + /** + * Get the length of the parameter list. + * + * @return The length of the parameters. + */ public int length() { return params.length; } @@ -27,34 +38,34 @@ public class CLParameters { * Mostly, this just fills in V and # parameters. * * @param params - * The parameters of the directive. + * The parameters of the directive. * @param dirParams - * The parameters of the format string. + * The parameters of the format string. * * @return A set of CL parameters. */ public static CLParameters fromDirective(String[] params, Tape<Object> dirParams) { List<String> parameters = new ArrayList<>(); - for (String param : params) { - if (param.equalsIgnoreCase("V")) { + for(String param : params) { + if(param.equalsIgnoreCase("V")) { Object par = dirParams.item(); boolean succ = dirParams.right(); - if (!succ) { + if(!succ) { throw new IllegalStateException("Couldn't advance tape for parameter"); } - if (par == null) { + if(par == null) { throw new IllegalArgumentException( "Expected a format parameter for V inline parameter"); } - if (par instanceof Number) { + if(par instanceof Number) { int val = ((Number) par).intValue(); parameters.add(Integer.toString(val)); - } else if (par instanceof Character) { + } else if(par instanceof Character) { char ch = ((Character) par); parameters.add(Character.toString(ch)); @@ -62,7 +73,7 @@ public class CLParameters { throw new IllegalArgumentException( "Incorrect type of parameter for V inline parameter"); } - } else if (param.equals("#")) { + } else if(param.equals("#")) { parameters.add(Integer.toString(dirParams.position())); } else { parameters.add(param); @@ -72,39 +83,89 @@ public class CLParameters { return new CLParameters(parameters.toArray(new String[0])); } + /** + * Get an optional character parameter with a default value. + * + * @param idx + * The index the parameter is at. + * @param paramName + * The name of the parameter. + * @param directive + * The directive this parameter belongs to. + * @param def + * The default value for the parameter. + * @return The value of the parameter if it exists, or the default + * otherwise. + */ public char getCharDefault(int idx, String paramName, char directive, char def) { - if (!params[idx].equals("")) { + if(!params[idx].equals("")) { return getChar(idx, paramName, directive); } return def; } + /** + * Get a mandatory character parameter. + * + * @param idx + * The index the parameter is at. + * @param paramName + * The name of the parameter. + * @param directive + * The directive this parameter belongs to. + * @return The value for the parameter. + */ public char getChar(int idx, String paramName, char directive) { String param = params[idx]; - if (!param.startsWith("'")) { + if(!param.startsWith("'")) { throw new IllegalArgumentException( String.format("Invalid %s %s to %c directive", paramName, param, directive)); } return param.charAt(1); } - + + /** + * Get an optional integer parameter with a default value. + * + * @param idx + * The index the parameter is at. + * @param paramName + * The name of the parameter. + * @param directive + * The directive this parameter belongs to. + * @param def + * The default value for the parameter. + * @return The value of the parameter if it exists, or the default + * otherwise. + */ public int getIntDefault(int idx, String paramName, char directive, int def) { - if (!params[idx].equals("")) { - + if(!params[idx].equals("")) { + return getInt(idx, paramName, directive); } return def; } - + + /** + * Get a mandatory integer parameter. + * + * @param idx + * The index the parameter is at. + * @param paramName + * The name of the parameter. + * @param directive + * The directive this parameter belongs to. + * @return The value for the parameter. + */ public int getInt(int idx, String paramName, char directive) { String param = params[idx]; try { return Integer.parseInt(param); - } catch (NumberFormatException nfex) { + } catch(NumberFormatException nfex) { String msg = String.format("Invalid %s %s to %c directive", paramName, param, directive); IllegalArgumentException iaex = new IllegalArgumentException(msg); diff --git a/base/src/main/java/bjc/utils/ioutils/format/CharacterDirective.java b/base/src/main/java/bjc/utils/ioutils/format/CharacterDirective.java new file mode 100644 index 0000000..7ff74bb --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/CharacterDirective.java @@ -0,0 +1,34 @@ +package bjc.utils.ioutils.format; + +import bjc.utils.esodata.Tape; + +import java.util.IllegalFormatConversionException; +import java.util.regex.Matcher; + +class CharacterDirective implements Directive { + + @Override + public void format(StringBuffer buff, Object parm, CLModifiers mods, CLParameters arrParams, + Tape<Object> tParams, Matcher dirMatcher, CLFormatter fmt) { + CLFormatter.checkItem(parm, 'C'); + + if(!(parm instanceof Character)) { + throw new IllegalFormatConversionException('C', parm.getClass()); + } + + char ch = (Character) parm; + int codepoint = ch; + + if(mods.colonMod) { + /* + * Colon mod means print Unicode character name. + */ + buff.append(Character.getName(codepoint)); + } else { + buff.append(ch); + } + + tParams.right(); + } + +} diff --git a/base/src/main/java/bjc/utils/ioutils/format/ConditionalDirective.java b/base/src/main/java/bjc/utils/ioutils/format/ConditionalDirective.java new file mode 100644 index 0000000..5ae842c --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/ConditionalDirective.java @@ -0,0 +1,117 @@ +package bjc.utils.ioutils.format; + +import bjc.utils.esodata.Tape; + +import java.util.ArrayList; +import java.util.IllegalFormatConversionException; +import java.util.List; +import java.util.regex.Matcher; + +class ConditionalDirective implements Directive { + + @Override + public void format(StringBuffer sb, Object item, CLModifiers mods, CLParameters arrParams, + Tape<Object> formatParams, Matcher dirMatcher, CLFormatter fmt) { + StringBuffer condBody = new StringBuffer(); + + List<String> clauses = new ArrayList<>(); + String defClause = null; + boolean isDefault = false; + + while(dirMatcher.find()) { + /* Process a list of clauses. */ + String dirName = dirMatcher.group("name"); + String dirMods = dirMatcher.group("modifiers"); + + if(dirName != null) { + /* Append everything up to this directive. */ + dirMatcher.appendReplacement(condBody, ""); + + if(dirName.equals("]")) { + /* End the conditional. */ + String clause = condBody.toString(); + if(isDefault) { + defClause = clause; + } else { + clauses.add(clause); + } + + break; + } else if(dirName.equals(";")) { + /* End the clause. */ + String clause = condBody.toString(); + if(isDefault) { + defClause = clause; + } else { + clauses.add(clause); + } + + /* + * Mark the next clause as the default. + */ + if(dirMods.contains(":")) { + isDefault = true; + } + } else { + /* Not a special directive. */ + condBody.append(dirMatcher.group()); + } + } + } + + Object par = formatParams.item(); + if(mods.colonMod) { + formatParams.right(); + + if(par == null) { + throw new IllegalArgumentException("No parameter provided for [ directive."); + } else if(!(par instanceof Boolean)) { + throw new IllegalFormatConversionException('[', par.getClass()); + } + boolean res = (Boolean) par; + + String frmt; + if(res) + frmt = clauses.get(1); + else + frmt = clauses.get(0); + + fmt.doFormatString(frmt, sb, formatParams); + } else if(mods.atMod) { + if(par == null) { + throw new IllegalArgumentException("No parameter provided for [ directive."); + } else if(!(par instanceof Boolean)) { + throw new IllegalFormatConversionException('[', par.getClass()); + } + boolean res = (Boolean) par; + + if(res) { + fmt.doFormatString(clauses.get(0), sb, formatParams); + } else { + formatParams.right(); + } + } else { + int res; + if(arrParams.length() > 1) { + res = arrParams.getInt(0, "conditional choice", '['); + } else { + if(par == null) { + throw new IllegalArgumentException("No parameter provided for [ directive."); + } else if(!(par instanceof Number)) { + throw new IllegalFormatConversionException('[', par.getClass()); + } + res = ((Number) par).intValue(); + + formatParams.right(); + } + + if(res < 0 || res > clauses.size()) { + if(defClause != null) fmt.doFormatString(defClause, sb, formatParams); + } else { + fmt.doFormatString(clauses.get(res), sb, formatParams); + } + } + return; + } + +} diff --git a/base/src/main/java/bjc/utils/ioutils/format/Directive.java b/base/src/main/java/bjc/utils/ioutils/format/Directive.java index aa6695f..0b1e889 100644 --- a/base/src/main/java/bjc/utils/ioutils/format/Directive.java +++ b/base/src/main/java/bjc/utils/ioutils/format/Directive.java @@ -1,14 +1,14 @@ -package bjc.utils.ioutils.format;
-
-import java.util.regex.Matcher;
-
-import bjc.utils.esodata.Tape;
-
-@FunctionalInterface
-public interface Directive {
- /*
- * @TODO fill in parameters
- */
- public void format(StringBuffer sb, Object item, CLModifiers mods,
- CLParameters arrParams, Tape<Object> tParams, Matcher dirMatcher);
+package bjc.utils.ioutils.format; + +import java.util.regex.Matcher; + +import bjc.utils.esodata.Tape; + +@FunctionalInterface +public interface Directive { + /* + * @TODO fill in parameters + */ + public void format(StringBuffer sb, Object item, CLModifiers mods, + CLParameters arrParams, Tape<Object> tParams, Matcher dirMatcher, CLFormatter fmt); }
\ No newline at end of file diff --git a/base/src/main/java/bjc/utils/ioutils/format/EscapeDirective.java b/base/src/main/java/bjc/utils/ioutils/format/EscapeDirective.java new file mode 100644 index 0000000..4f44479 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/EscapeDirective.java @@ -0,0 +1,42 @@ +package bjc.utils.ioutils.format; + +import bjc.utils.esodata.Tape; + +import java.util.regex.Matcher; + +class EscapeDirective implements Directive { + + @Override + public void format(StringBuffer sb, Object item, CLModifiers mods, CLParameters params, Tape<Object> formatParams, + Matcher dirMatcher, CLFormatter fmt) { + boolean shouldExit; + + switch(params.length()) { + case 0: + shouldExit = formatParams.size() == 0; + break; + case 1: + int num = params.getInt(0, "condition count", '^'); + shouldExit = num == 0; + break; + case 2: + int left = params.getInt(0, "left-hand condition", '^'); + int right = params.getInt(1, "right-hand condition", '^'); + shouldExit = left == right; + break; + case 3: + default: + int low = params.getInt(0, "lower-bound condition", '^'); + int mid = params.getInt(1, "interval condition", '^'); + int high = params.getInt(2, "upper-bound condition", '^'); + shouldExit = (low <= mid) && (mid <= high); + break; + } + + /* At negates it. */ + if(mods.atMod) shouldExit = !shouldExit; + + if(shouldExit) throw new EscapeException(mods.colonMod); + } + +} diff --git a/base/src/main/java/bjc/utils/ioutils/format/FreshlineDirective.java b/base/src/main/java/bjc/utils/ioutils/format/FreshlineDirective.java new file mode 100644 index 0000000..8ae0c72 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/FreshlineDirective.java @@ -0,0 +1,25 @@ +package bjc.utils.ioutils.format; + +import bjc.utils.esodata.Tape; + +import java.util.regex.Matcher; + +class FreshlineDirective implements Directive { + + @Override + public void format(StringBuffer buff, Object item, CLModifiers mods, CLParameters params, Tape<Object> tParams, + Matcher dirMatcher, CLFormatter fmt) { + int nTimes = 1; + + if(params.length() > 1) { + nTimes = params.getInt(0, "occurance count", '&'); + } + + if(buff.charAt(buff.length() - 1) == '\n') nTimes -= 1; + + for(int i = 0; i < nTimes; i++) { + buff.append("\n"); + } + } + +} diff --git a/base/src/main/java/bjc/utils/ioutils/format/GeneralNumberDirective.java b/base/src/main/java/bjc/utils/ioutils/format/GeneralNumberDirective.java new file mode 100644 index 0000000..6a90f94 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/GeneralNumberDirective.java @@ -0,0 +1,45 @@ +package bjc.utils.ioutils.format; + +import bjc.utils.math.NumberUtils; + +abstract class GeneralNumberDirective implements Directive { + protected static void handleNumberDirective(StringBuffer buff, CLModifiers mods, CLParameters params, + int argidx, long val, int radix) { + /* + * Initialize the two padding related parameters, and then fill + * them in from the directive parameters if they are present. + */ + int mincol = 0; + char padchar = ' '; + if(params.length() > (argidx + 2)) { + mincol = params.getIntDefault(argidx + 1, "minimum column count", 'R', 0); + } + if(params.length() > (argidx + 3)) { + padchar = params.getCharDefault(argidx + 2, "padding character", 'R', ' '); + } + + String res; + + if(mods.colonMod) { + /* + * We're doing commas, so check if the two comma-related + * parameters were supplied. + */ + int commaInterval = 0; + char commaChar = ','; + if(params.length() > (argidx + 3)) { + commaChar = params.getCharDefault((argidx + 3), "comma character", 'R', ' '); + } + if(params.length() > (argidx + 4)) { + commaInterval = params.getIntDefault((argidx + 4), "comma interval", 'R', 0); + } + + res = NumberUtils.toCommaString(val, mincol, padchar, commaInterval, commaChar, mods.atMod, + radix); + } else { + res = NumberUtils.toNormalString(val, mincol, padchar, mods.atMod, radix); + } + + buff.append(res); + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/format/GotoDirective.java b/base/src/main/java/bjc/utils/ioutils/format/GotoDirective.java new file mode 100644 index 0000000..b09053e --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/GotoDirective.java @@ -0,0 +1,37 @@ +package bjc.utils.ioutils.format; + +import bjc.utils.esodata.Tape; + +import java.util.regex.Matcher; + +class GotoDirective implements Directive { + + @Override + public void format(StringBuffer sb, Object item, CLModifiers mods, CLParameters params, Tape<Object> formatParams, + Matcher dirMatcher, CLFormatter fmt) { + if(mods.colonMod) { + int num = 1; + if(params.length() > 1) { + num = params.getIntDefault(0, "number of arguments backward", '*', 1); + } + + formatParams.left(num); + } else if(mods.atMod) { + int num = 0; + if(params.length() > 1) { + num = params.getIntDefault(0, "argument index", '*', 0); + } + + formatParams.first(); + formatParams.right(num); + } else { + int num = 1; + if(params.length() > 1) { + num = params.getIntDefault(0, "number of arguments forward", '*', 1); + } + + formatParams.right(num); + } + } + +} diff --git a/base/src/main/java/bjc/utils/ioutils/format/IterationDirective.java b/base/src/main/java/bjc/utils/ioutils/format/IterationDirective.java new file mode 100644 index 0000000..353ff94 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/IterationDirective.java @@ -0,0 +1,78 @@ +package bjc.utils.ioutils.format; + +import bjc.utils.esodata.Tape; + +import java.util.ArrayList; +import java.util.IllegalFormatConversionException; +import java.util.List; +import java.util.regex.Matcher; + +class IterationDirective implements Directive { + + @Override + public void format(StringBuffer sb, Object item, CLModifiers mods, CLParameters arrParams, Tape<Object> tParams, + Matcher dirMatcher, CLFormatter fmt) { + CLFormatter.checkItem(item, '{'); + + StringBuffer condBody = new StringBuffer(); + + while(dirMatcher.find()) { + /* Process a list of clauses. */ + String dirName = dirMatcher.group("name"); + + if(dirName != null) { + /* Append everything up to this directive. */ + dirMatcher.appendReplacement(condBody, ""); + + if(dirName.equals("}")) { + /* End the iteration. */ + break; + } + + /* Not a special directive. */ + condBody.append(dirMatcher.group()); + } + } + + String frmt = condBody.toString(); + Object iter = item; + + if(frmt.equals("")) { + /* Grab an argument. */ + if(!(item instanceof String)) { + throw new IllegalFormatConversionException('{', String.class); + } + + frmt = (String) item; + + if(!tParams.right()) { + throw new IllegalArgumentException("Not enough parameters to '{' directive"); + } + + iter = tParams.item(); + } + + int maxItr = Integer.MAX_VALUE; + + if(arrParams.length() > 0) { + maxItr = arrParams.getInt(0, "maximum iterations", '{'); + } + + if(mods.atMod && mods.colonMod) { + + } else if(mods.atMod) { + + } else if(mods.colonMod) { + if(!(item instanceof Iterable<?>)) { + throw new IllegalFormatConversionException('{', item.getClass()); + } + } else { + if(!(item instanceof Iterable<?>)) { + throw new IllegalFormatConversionException('{', item.getClass()); + } + } + + tParams.right(); + } + +} diff --git a/base/src/main/java/bjc/utils/ioutils/format/LiteralDirective.java b/base/src/main/java/bjc/utils/ioutils/format/LiteralDirective.java new file mode 100644 index 0000000..77d26cc --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/LiteralDirective.java @@ -0,0 +1,33 @@ +package bjc.utils.ioutils.format; + +import bjc.utils.esodata.Tape; + +import java.util.regex.Matcher; + +class LiteralDirective implements Directive { + + private char directive; + private String lit; + + public LiteralDirective(String lit, char directive) { + this.directive = directive; + this.lit = lit; + } + + + @Override + public void format(StringBuffer buff, Object item, CLModifiers mods, CLParameters params, Tape<Object> tParams, + Matcher dirMatcher, CLFormatter fmt) { + int nTimes = 1; + + if(params.length() > 1) { + nTimes = params.getInt(0, "occurance count", directive); + } + + for(int i = 0; i < nTimes; i++) { + buff.append(lit); + } + + } + +} diff --git a/base/src/main/java/bjc/utils/ioutils/format/NumberDirective.java b/base/src/main/java/bjc/utils/ioutils/format/NumberDirective.java index d5d4b29..28e61ea 100644 --- a/base/src/main/java/bjc/utils/ioutils/format/NumberDirective.java +++ b/base/src/main/java/bjc/utils/ioutils/format/NumberDirective.java @@ -1,46 +1,34 @@ -package bjc.utils.ioutils.format;
-
-import java.util.regex.Matcher;
-
-import bjc.utils.esodata.Tape;
-import bjc.utils.ioutils.NumberUtils;
-
-public class NumberDirective implements Directive {
-
- @Override
- public void format(StringBuffer sb, Object item, CLModifiers mods, CLParameters params, Tape<Object> tParams,
- Matcher dirMatcher) {
- /*
- * Initialize the two padding related parameters, and then fill them in from the
- * directive parameters if they are present.
- */
- int mincol = 0;
- char padchar = ' ';
- if (params.length() > (argidx + 2)) {
- mincol = params.getIntDefault(argidx + 1, "minimum column count", 'R', 0);
- }
- if (params.length() > (argidx + 3)) {
- padchar = params.getCharDefault(argidx + 2, "padding character", 'R', ' ');
- }
-
- if (mods.colonMod) {
- /*
- * We're doing commas, so check if the two comma-related parameters were
- * supplied.
- */
- int commaInterval = 0;
- char commaChar = ',';
- if (params.length() > (argidx + 3)) {
- commaChar = params.getCharDefault((argidx + 3), "comma character", 'R', ' ');
- }
- if (params.length() > (argidx + 4)) {
- commaInterval = params.getIntDefault((argidx + 4), "comma interval", 'R', 0);
- }
-
- NumberUtils.toCommaString(val, mincol, padchar, commaInterval, commaChar, mods.atMod, radix);
- } else {
- NumberUtils.toNormalString(val, mincol, padchar, mods.atMod, radix);
- }
- }
-
-}
+package bjc.utils.ioutils.format; + +import java.util.IllegalFormatConversionException; +import java.util.regex.Matcher; + +import bjc.utils.esodata.Tape; + +class NumberDirective extends GeneralNumberDirective { + + public NumberDirective(int argidx, int radix) { + this.argidx = argidx; + this.radix = radix; + } + + private int argidx; + private int radix; + + @Override + public void format(StringBuffer sb, Object item, CLModifiers mods, CLParameters params, Tape<Object> tParams, + Matcher dirMatcher, CLFormatter fmt) { + CLFormatter.checkItem(item, 'B'); + + if(!(item instanceof Number)) { + throw new IllegalFormatConversionException('B', item.getClass()); + } + + long val = ((Number) item).longValue(); + + handleNumberDirective(sb, mods, params, argidx, val, radix); + + tParams.right(); + } + +} diff --git a/base/src/main/java/bjc/utils/ioutils/format/RadixDirective.java b/base/src/main/java/bjc/utils/ioutils/format/RadixDirective.java new file mode 100644 index 0000000..3742582 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/RadixDirective.java @@ -0,0 +1,44 @@ +package bjc.utils.ioutils.format; + +import bjc.utils.esodata.Tape; +import bjc.utils.math.NumberUtils; + +import java.util.IllegalFormatConversionException; +import java.util.regex.Matcher; + +class RadixDirective extends GeneralNumberDirective { + + @Override + public void format(StringBuffer buff, Object arg, CLModifiers mods, CLParameters params, Tape<Object> tParams, + Matcher dirMatcher, CLFormatter fmt) { + CLFormatter.checkItem(arg, 'R'); + + if(!(arg instanceof Number)) { + throw new IllegalFormatConversionException('R', arg.getClass()); + } + + /* + * @TODO see if this is the way we want to do this. + */ + long val = ((Number) arg).longValue(); + + if(params.length() == 0) { + if(mods.atMod) { + buff.append(NumberUtils.toRoman(val, mods.colonMod)); + } else if(mods.colonMod) { + buff.append(NumberUtils.toOrdinal(val)); + } else { + buff.append(NumberUtils.toCardinal(val)); + } + } else { + if(params.length() < 1) throw new IllegalArgumentException( + "R directive requires at least one parameter, the radix"); + + int radix = params.getInt(0, "radix", 'R'); + + handleNumberDirective(buff, mods, params, 0, val, radix); + } + + tParams.right(); + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/NumberUtils.java b/base/src/main/java/bjc/utils/math/NumberUtils.java index 1b754e2..9391040 100644 --- a/base/src/main/java/bjc/utils/ioutils/NumberUtils.java +++ b/base/src/main/java/bjc/utils/math/NumberUtils.java @@ -1,4 +1,4 @@ -package bjc.utils.ioutils; +package bjc.utils.math; import java.util.Map; import java.util.function.BiFunction; |
