From 92ec9adfcb115fe86d5ba27fcc089db027374d6a Mon Sep 17 00:00:00 2001 From: student Date: Fri, 9 Feb 2018 11:52:03 -0500 Subject: Work on CL format strings --- .../main/java/bjc/utils/ioutils/CLFormatter.java | 541 --------------------- .../main/java/bjc/utils/ioutils/CLParameters.java | 116 ----- .../utils/ioutils/format/AestheticDirective.java | 61 +++ .../java/bjc/utils/ioutils/format/CLFormatter.java | 456 +++++++++++++++++ .../java/bjc/utils/ioutils/format/CLModifiers.java | 22 + .../bjc/utils/ioutils/format/CLParameters.java | 116 +++++ .../java/bjc/utils/ioutils/format/Directive.java | 14 + .../bjc/utils/ioutils/format/EscapeException.java | 15 + .../bjc/utils/ioutils/format/NumberDirective.java | 46 ++ 9 files changed, 730 insertions(+), 657 deletions(-) delete mode 100644 base/src/main/java/bjc/utils/ioutils/CLFormatter.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/CLParameters.java create mode 100644 base/src/main/java/bjc/utils/ioutils/format/AestheticDirective.java create mode 100644 base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java create mode 100644 base/src/main/java/bjc/utils/ioutils/format/CLModifiers.java create mode 100644 base/src/main/java/bjc/utils/ioutils/format/CLParameters.java create mode 100644 base/src/main/java/bjc/utils/ioutils/format/Directive.java create mode 100644 base/src/main/java/bjc/utils/ioutils/format/EscapeException.java create mode 100644 base/src/main/java/bjc/utils/ioutils/format/NumberDirective.java (limited to 'base') diff --git a/base/src/main/java/bjc/utils/ioutils/CLFormatter.java b/base/src/main/java/bjc/utils/ioutils/CLFormatter.java deleted file mode 100644 index 1f65607..0000000 --- a/base/src/main/java/bjc/utils/ioutils/CLFormatter.java +++ /dev/null @@ -1,541 +0,0 @@ -package bjc.utils.ioutils; - -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.esodata.SingleTape; - -import static bjc.utils.PropertyDB.applyFormat; - -import static bjc.utils.PropertyDB.getRegex; - -public class CLFormatter { - public static class CLModifiers { - public final boolean atMod; - public final boolean colonMod; - - public CLModifiers(boolean at, boolean colon) { - atMod = at; - colonMod = colon; - } - - public static CLModifiers fromString(String modString) { - boolean atMod = false; - boolean colonMod = false; - if (modString != null) { - atMod = modString.contains("@"); - colonMod = modString.contains(":"); - } - - return new CLModifiers(atMod, colonMod); - } - } - - public static class EscapeException extends RuntimeException { - private static final long serialVersionUID = -4552821131068559005L; - - public final boolean endIteration; - - public EscapeException() { - endIteration = false; - } - - public EscapeException(boolean end) { - endIteration = end; - } - } - - @FunctionalInterface - public interface Directive { - /* - * @TODO fill in parameters - */ - public void format(); - } - - 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 Map extraDirectives; - - public CLFormatter() { - extraDirectives = new HashMap<>(); - } - - private 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 tParams = new SingleTape<>(params); - - doFormatString(format, sb, tParams); - - return sb.toString(); - } - - private void doFormatString(String format, StringBuffer sb, Tape 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("(?": - /* - * @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 handleAestheticDirective(StringBuffer buff, Object item, CLModifiers mods, CLParameters params) { - 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); - } - } - } - } - - private void handleGotoDirective(CLModifiers mods, CLParameters params, Tape 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 formatParams, Matcher dirMatcher) { - StringBuffer condBody = new StringBuffer(); - - List 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 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); - } - -} diff --git a/base/src/main/java/bjc/utils/ioutils/CLParameters.java b/base/src/main/java/bjc/utils/ioutils/CLParameters.java deleted file mode 100644 index 9d0c9c6..0000000 --- a/base/src/main/java/bjc/utils/ioutils/CLParameters.java +++ /dev/null @@ -1,116 +0,0 @@ -package bjc.utils.ioutils; - -import java.util.ArrayList; -import java.util.List; - -import bjc.utils.esodata.Tape; - -/** - * Represents a set of parameters to a CL format directive. - * - * @author Benjamin Culkin - */ -public class CLParameters { - private String[] params; - - public CLParameters(String[] params) { - this.params = params; - } - - public int length() { - return params.length; - } - - /** - * Creates a set of parameters from an array of parameters. - * - * Mostly, this just fills in V and # parameters. - * - * @param params - * The parameters of the directive. - * @param dirParams - * The parameters of the format string. - * - * @return A set of CL parameters. - */ - public static CLParameters fromDirective(String[] params, Tape dirParams) { - List parameters = new ArrayList<>(); - - for (String param : params) { - if (param.equalsIgnoreCase("V")) { - Object par = dirParams.item(); - boolean succ = dirParams.right(); - - if (!succ) { - throw new IllegalStateException("Couldn't advance tape for parameter"); - } - - if (par == null) { - throw new IllegalArgumentException( - "Expected a format parameter for V inline parameter"); - } - - if (par instanceof Number) { - int val = ((Number) par).intValue(); - - parameters.add(Integer.toString(val)); - } else if (par instanceof Character) { - char ch = ((Character) par); - - parameters.add(Character.toString(ch)); - } else { - throw new IllegalArgumentException( - "Incorrect type of parameter for V inline parameter"); - } - } else if (param.equals("#")) { - parameters.add(Integer.toString(dirParams.position())); - } else { - parameters.add(param); - } - } - - return new CLParameters(parameters.toArray(new String[0])); - } - - public char getCharDefault(int idx, String paramName, char directive, char def) { - if (!params[idx].equals("")) { - return getChar(idx, paramName, directive); - } - - return def; - } - - public char getChar(int idx, String paramName, char directive) { - String param = params[idx]; - - if (!param.startsWith("'")) { - throw new IllegalArgumentException( - String.format("Invalid %s %s to %c directive", paramName, param, directive)); - } - - return param.charAt(1); - } - - public int getIntDefault(int idx, String paramName, char directive, int def) { - if (!params[idx].equals("")) { - - } - - return def; - } - - public int getInt(int idx, String paramName, char directive) { - String param = params[idx]; - - try { - return Integer.parseInt(param); - } catch (NumberFormatException nfex) { - String msg = String.format("Invalid %s %s to %c directive", paramName, param, directive); - - IllegalArgumentException iaex = new IllegalArgumentException(msg); - iaex.initCause(nfex); - - throw iaex; - } - } -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/AestheticDirective.java b/base/src/main/java/bjc/utils/ioutils/format/AestheticDirective.java new file mode 100644 index 0000000..869834b --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/AestheticDirective.java @@ -0,0 +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 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(); + } + +} diff --git a/base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java b/base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java new file mode 100644 index 0000000..8abc0ff --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java @@ -0,0 +1,456 @@ +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 builtinDirectives; + private Map 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 tParams = new SingleTape<>(params); + + doFormatString(format, sb, tParams); + + return sb.toString(); + } + + private void doFormatString(String format, StringBuffer sb, Tape 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("(?": + /* + * @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 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 formatParams, Matcher dirMatcher) { + StringBuffer condBody = new StringBuffer(); + + List 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 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); + } + +} diff --git a/base/src/main/java/bjc/utils/ioutils/format/CLModifiers.java b/base/src/main/java/bjc/utils/ioutils/format/CLModifiers.java new file mode 100644 index 0000000..a695f2f --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/CLModifiers.java @@ -0,0 +1,22 @@ +package bjc.utils.ioutils.format; + +public class CLModifiers { + public final boolean atMod; + public final boolean colonMod; + + public CLModifiers(boolean at, boolean colon) { + atMod = at; + colonMod = colon; + } + + public static CLModifiers fromString(String modString) { + boolean atMod = false; + boolean colonMod = false; + if (modString != null) { + atMod = modString.contains("@"); + colonMod = modString.contains(":"); + } + + return new CLModifiers(atMod, colonMod); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/ioutils/format/CLParameters.java b/base/src/main/java/bjc/utils/ioutils/format/CLParameters.java new file mode 100644 index 0000000..e3a03f6 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/CLParameters.java @@ -0,0 +1,116 @@ +package bjc.utils.ioutils.format; + +import java.util.ArrayList; +import java.util.List; + +import bjc.utils.esodata.Tape; + +/** + * Represents a set of parameters to a CL format directive. + * + * @author Benjamin Culkin + */ +public class CLParameters { + private String[] params; + + public CLParameters(String[] params) { + this.params = params; + } + + public int length() { + return params.length; + } + + /** + * Creates a set of parameters from an array of parameters. + * + * Mostly, this just fills in V and # parameters. + * + * @param params + * The parameters of the directive. + * @param dirParams + * The parameters of the format string. + * + * @return A set of CL parameters. + */ + public static CLParameters fromDirective(String[] params, Tape dirParams) { + List parameters = new ArrayList<>(); + + for (String param : params) { + if (param.equalsIgnoreCase("V")) { + Object par = dirParams.item(); + boolean succ = dirParams.right(); + + if (!succ) { + throw new IllegalStateException("Couldn't advance tape for parameter"); + } + + if (par == null) { + throw new IllegalArgumentException( + "Expected a format parameter for V inline parameter"); + } + + if (par instanceof Number) { + int val = ((Number) par).intValue(); + + parameters.add(Integer.toString(val)); + } else if (par instanceof Character) { + char ch = ((Character) par); + + parameters.add(Character.toString(ch)); + } else { + throw new IllegalArgumentException( + "Incorrect type of parameter for V inline parameter"); + } + } else if (param.equals("#")) { + parameters.add(Integer.toString(dirParams.position())); + } else { + parameters.add(param); + } + } + + return new CLParameters(parameters.toArray(new String[0])); + } + + public char getCharDefault(int idx, String paramName, char directive, char def) { + if (!params[idx].equals("")) { + return getChar(idx, paramName, directive); + } + + return def; + } + + public char getChar(int idx, String paramName, char directive) { + String param = params[idx]; + + if (!param.startsWith("'")) { + throw new IllegalArgumentException( + String.format("Invalid %s %s to %c directive", paramName, param, directive)); + } + + return param.charAt(1); + } + + public int getIntDefault(int idx, String paramName, char directive, int def) { + if (!params[idx].equals("")) { + + } + + return def; + } + + public int getInt(int idx, String paramName, char directive) { + String param = params[idx]; + + try { + return Integer.parseInt(param); + } catch (NumberFormatException nfex) { + String msg = String.format("Invalid %s %s to %c directive", paramName, param, directive); + + IllegalArgumentException iaex = new IllegalArgumentException(msg); + iaex.initCause(nfex); + + throw iaex; + } + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/format/Directive.java b/base/src/main/java/bjc/utils/ioutils/format/Directive.java new file mode 100644 index 0000000..aa6695f --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/Directive.java @@ -0,0 +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 tParams, Matcher dirMatcher); +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/ioutils/format/EscapeException.java b/base/src/main/java/bjc/utils/ioutils/format/EscapeException.java new file mode 100644 index 0000000..a1df55a --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/EscapeException.java @@ -0,0 +1,15 @@ +package bjc.utils.ioutils.format; + +public class EscapeException extends RuntimeException { + private static final long serialVersionUID = -4552821131068559005L; + + public final boolean endIteration; + + public EscapeException() { + endIteration = false; + } + + public EscapeException(boolean end) { + endIteration = end; + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/ioutils/format/NumberDirective.java b/base/src/main/java/bjc/utils/ioutils/format/NumberDirective.java new file mode 100644 index 0000000..d5d4b29 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/format/NumberDirective.java @@ -0,0 +1,46 @@ +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 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); + } + } + +} -- cgit v1.2.3