From 14c444db5bc8dac003cd31283de6484264377004 Mon Sep 17 00:00:00 2001 From: "Benjamin J. Culkin" Date: Sun, 16 Sep 2018 21:28:42 -0300 Subject: Move CL format to submodule --- .../main/java/bjc/utils/cli/objects/DefineCLI.java | 15 +- .../java/bjc/utils/ioutils/format/CLFormatter.java | 282 --------------------- .../java/bjc/utils/ioutils/format/CLModifiers.java | 66 ----- .../bjc/utils/ioutils/format/CLParameters.java | 224 ---------------- .../bjc/utils/ioutils/format/EscapeException.java | 33 --- .../format/directives/AestheticDirective.java | 74 ------ .../ioutils/format/directives/CaseDirective.java | 117 --------- .../format/directives/CharacterDirective.java | 45 ---- .../format/directives/ConditionalDirective.java | 193 -------------- .../utils/ioutils/format/directives/Directive.java | 72 ------ .../ioutils/format/directives/EscapeDirective.java | 59 ----- .../format/directives/FreshlineDirective.java | 34 --- .../format/directives/GeneralNumberDirective.java | 55 ---- .../ioutils/format/directives/GotoDirective.java | 47 ---- .../format/directives/IterationDirective.java | 171 ------------- .../format/directives/LiteralDirective.java | 51 ---- .../ioutils/format/directives/NumberDirective.java | 57 ----- .../ioutils/format/directives/RadixDirective.java | 55 ---- .../format/directives/RecursiveDirective.java | 49 ---- .../format/directives/TabulateDirective.java | 80 ------ .../bjc/utils/test/ioutils/CLFormatterTest.java | 128 ---------- clformat/.gitignore | 1 + clformat/pom.xml | 89 +++++++ clformat/src/main/java/bjc/App.java | 13 + .../java/bjc/utils/ioutils/format/CLFormatter.java | 282 +++++++++++++++++++++ .../java/bjc/utils/ioutils/format/CLModifiers.java | 66 +++++ .../bjc/utils/ioutils/format/CLParameters.java | 224 ++++++++++++++++ .../bjc/utils/ioutils/format/EscapeException.java | 33 +++ .../format/directives/AestheticDirective.java | 74 ++++++ .../ioutils/format/directives/CaseDirective.java | 117 +++++++++ .../format/directives/CharacterDirective.java | 45 ++++ .../format/directives/ConditionalDirective.java | 193 ++++++++++++++ .../utils/ioutils/format/directives/Directive.java | 72 ++++++ .../ioutils/format/directives/EscapeDirective.java | 59 +++++ .../format/directives/FreshlineDirective.java | 34 +++ .../format/directives/GeneralNumberDirective.java | 55 ++++ .../ioutils/format/directives/GotoDirective.java | 47 ++++ .../format/directives/IterationDirective.java | 171 +++++++++++++ .../format/directives/LiteralDirective.java | 51 ++++ .../ioutils/format/directives/NumberDirective.java | 57 +++++ .../ioutils/format/directives/RadixDirective.java | 55 ++++ .../format/directives/RecursiveDirective.java | 49 ++++ .../format/directives/TabulateDirective.java | 80 ++++++ clformat/src/test/java/bjc/AppTest.java | 20 ++ .../bjc/utils/test/ioutils/CLFormatterTest.java | 128 ++++++++++ pom.xml | 9 +- 46 files changed, 2027 insertions(+), 1904 deletions(-) delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/CLModifiers.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/CLParameters.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/EscapeException.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/AestheticDirective.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/CaseDirective.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/CharacterDirective.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/ConditionalDirective.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/Directive.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/EscapeDirective.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/FreshlineDirective.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/GeneralNumberDirective.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/GotoDirective.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/IterationDirective.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/LiteralDirective.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/NumberDirective.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/RadixDirective.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/RecursiveDirective.java delete mode 100644 base/src/main/java/bjc/utils/ioutils/format/directives/TabulateDirective.java delete mode 100644 base/src/test/java/bjc/utils/test/ioutils/CLFormatterTest.java create mode 100644 clformat/.gitignore create mode 100644 clformat/pom.xml create mode 100644 clformat/src/main/java/bjc/App.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/CLFormatter.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/CLModifiers.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/CLParameters.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/EscapeException.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/AestheticDirective.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/CaseDirective.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/CharacterDirective.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/ConditionalDirective.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/Directive.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/EscapeDirective.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/FreshlineDirective.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/GeneralNumberDirective.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/GotoDirective.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/IterationDirective.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/LiteralDirective.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/NumberDirective.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/RadixDirective.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/RecursiveDirective.java create mode 100644 clformat/src/main/java/bjc/utils/ioutils/format/directives/TabulateDirective.java create mode 100644 clformat/src/test/java/bjc/AppTest.java create mode 100644 clformat/src/test/java/bjc/utils/test/ioutils/CLFormatterTest.java diff --git a/base/src/main/java/bjc/utils/cli/objects/DefineCLI.java b/base/src/main/java/bjc/utils/cli/objects/DefineCLI.java index 6ebcf39..3a03d18 100644 --- a/base/src/main/java/bjc/utils/cli/objects/DefineCLI.java +++ b/base/src/main/java/bjc/utils/cli/objects/DefineCLI.java @@ -1,7 +1,6 @@ package bjc.utils.cli.objects; import bjc.utils.funcutils.StringUtils; -import bjc.utils.ioutils.format.CLFormatter; import java.io.IOException; import java.util.ArrayList; @@ -227,15 +226,15 @@ public class DefineCLI { } } - CLFormatter fmt = new CLFormatter(); + // CLFormatter fmt = new CLFormatter(); String formatted = ""; - try { - formatted = fmt.formatString(stat.formats.get(formatName), fillIns); - } catch (IOException ioex) { - LOGGER.severe(com.error("IOException formatting string: %s", ioex.getMessage())); - return FAIL; - } + // try { + // formatted = fmt.formatString(stat.formats.get(formatName), fillIns); + // } catch (IOException ioex) { + // LOGGER.severe(com.error("IOException formatting string: %s", ioex.getMessage())); + // return FAIL; + // } stat.strings.put(bindName, formatted); diff --git a/base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java b/base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java deleted file mode 100644 index 9e01ca6..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/CLFormatter.java +++ /dev/null @@ -1,282 +0,0 @@ -package bjc.utils.ioutils.format; - -import bjc.utils.esodata.SingleTape; -import bjc.utils.esodata.Tape; -import bjc.utils.ioutils.format.directives.*; -import bjc.utils.ioutils.ReportWriter; - -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; - -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.misc.PropertyDB.applyFormat; -import static bjc.utils.misc.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 directiveName = getRegex("clFormatName"); - - private static final String prefixList = applyFormat("delimSeparatedList", prefixParam, ","); - 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()); - // @NOTE 9/6/18 - // - // This is just an alias, not the actual S directive - builtinDirectives.put("S", new AestheticDirective()); - - builtinDirectives.put("C", new CharacterDirective()); - - builtinDirectives.put("B", new NumberDirective(-1, 2, 'B')); - builtinDirectives.put("O", new NumberDirective(-1, 8, 'O')); - builtinDirectives.put("D", new NumberDirective(-1, 10, 'D')); - builtinDirectives.put("X", new NumberDirective(-1, 16, 'X')); - - 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 RecursiveDirective()); - - builtinDirectives.put("*", new GotoDirective()); - - builtinDirectives.put("^", new EscapeDirective()); - builtinDirectives.put("[", new ConditionalDirective()); - builtinDirectives.put("{", new IterationDirective()); - builtinDirectives.put("(", new CaseDirective()); - - builtinDirectives.put("T", new TabulateDirective()); - } - - /** - * Create a new CL formatter. - */ - public CLFormatter() { - extraDirectives = new HashMap<>(); - } - - /** - * Check that an item is valid for a directive. - * - * @param itm - * The item to check. - * @param directive - * The directive to check for. - */ - public 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) throws IOException { - ReportWriter rw = new ReportWriter(new StringWriter()); - /* Put the parameters where we can easily handle them. */ - Tape tParams = new SingleTape<>(params); - - doFormatString(format, rw, tParams, true); - - return rw.toString(); - } - - /** - * 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, Iterable params) throws IOException { - ReportWriter rw = new ReportWriter(new StringWriter()); - - /* Put the parameters where we can easily handle them. */ - Tape tParams = new SingleTape<>(params); - - doFormatString(format, rw, tParams, true); - - return rw.toString(); - } - - /** - * Format a string in the style of CL's FORMAT. - * - * @param format - * The format string to use. - * @param params - * The parameters for the string. - */ - public void formatString(Writer target, String format, Object... params) throws IOException { - ReportWriter rw = new ReportWriter(target); - /* Put the parameters where we can easily handle them. */ - Tape tParams = new SingleTape<>(params); - - doFormatString(format, rw, tParams, true); - } - - /** - * 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 void formatString(Writer target, String format, Iterable params) throws IOException { - ReportWriter rw = new ReportWriter(target); - - /* Put the parameters where we can easily handle them. */ - Tape tParams = new SingleTape<>(params); - - doFormatString(format, rw, tParams, true); - } - - /** - * Fill in a partially started format string. - * - * Used mostly for directives that require formatting again with a - * different string. - * - * @param format - * The format to use. - * @param sb - * The buffer to file output into. - * @param tParams - * The parameters to use. - */ - public void doFormatString(String format, ReportWriter rw, Tape tParams, boolean isToplevel) throws IOException { - Matcher dirMatcher = pFormatDirective.matcher(format); - - // We need this StringBuffer to use appendReplacement and stuff - // from Matcher. The fact that for some reason, StringBuffer is - // final prevents us from using our own dummy StringBuffer that - // auto-flushes to our output stream, so we have to do it - // ourselves. - StringBuffer sb = new StringBuffer(); - - boolean doTail = true; - try { - while(dirMatcher.find()) { - dirMatcher.appendReplacement(sb, ""); - rw.writeBuffer(sb); - - String dirName = dirMatcher.group("name"); - String dirFunc = dirMatcher.group("funcname"); - String dirMods = dirMatcher.group("modifiers"); - String dirParams = dirMatcher.group("params"); - - if(dirMods == null) dirMods = ""; - if(dirParams == null) dirParams = ""; - - CLParameters arrParams = CLParameters.fromDirective(dirParams, 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(rw, item, mods, arrParams, tParams, dirMatcher, - this); - - continue; - } - - if(builtinDirectives.containsKey(dirName)) { - // System.err.printf("Executing directive %s (%s) (%d to %d) from string %s\n", dirName, dirMatcher.group(), dirMatcher.start(), dirMatcher.end(), format); - - builtinDirectives.get(dirName).format(rw, - item, mods, arrParams, tParams, dirMatcher, this); - - continue; - } - - if(dirName == null) dirName = ""; - - switch(dirName) { - case "]": - throw new IllegalArgumentException("Found conditional-end outside of conditional."); - case ";": - throw new IllegalArgumentException( - "Found seperator outside of block."); - case "}": - throw new IllegalArgumentException("Found iteration-end outside of iteration"); - case "<": - case ">": - 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 "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"); - case "P": - throw new IllegalArgumentException("These directives aren't implemented yet"); - case ")": - throw new IllegalArgumentException("Case-conversion end outside of case conversion"); - case "\n": - /* - * Ignored newline. - */ - break; - default: - String msg = String.format("Unknown format directive '%s'", dirName); - throw new IllegalArgumentException(msg); - } - } - } catch (EscapeException eex) { - if (!isToplevel) throw eex; - - doTail = false; - } - - if (doTail) dirMatcher.appendTail(sb); - rw.writeBuffer(sb); - } -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/CLModifiers.java b/base/src/main/java/bjc/utils/ioutils/format/CLModifiers.java deleted file mode 100644 index 68127b6..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/CLModifiers.java +++ /dev/null @@ -1,66 +0,0 @@ -package bjc.utils.ioutils.format; - -/** - * A collection of the modifiers attached to a CL format directive. - * - * @author EVE - * - */ -public class CLModifiers { - /** - * Whether the at mod is on. - */ - public final boolean atMod; - /** - * Whether the colon mod is on. - */ - public final boolean colonMod; - /** - * Whether the dollar mod is on. - */ - public final boolean dollarMod; - /** - * Whether the star mod is on. - */ - public final boolean starMod; - - /** - * Create a new set of CL modifiers. - * - * @param at - * The state of the at mod. - * @param colon - * The state of the colon mod. - * @param dollar - * The state of the dollar mod. - */ - public CLModifiers(boolean at, boolean colon, boolean dollar, boolean star) { - atMod = at; - colonMod = colon; - dollarMod = dollar; - starMod = star; - } - - /** - * Create a set of modifiers from a modifier string. - * - * @param modString - * The string to parse modifiers from. - * @return A set of modifiers matching the string. - */ - public static CLModifiers fromString(String modString) { - boolean atMod = false; - boolean colonMod = false; - boolean dollarMod = false; - boolean starMod = false; - - if(modString != null) { - atMod = modString.contains("@"); - colonMod = modString.contains(":"); - dollarMod = modString.contains("$"); - starMod = modString.contains("*"); - } - - return new CLModifiers(atMod, colonMod, dollarMod, starMod); - } -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/CLParameters.java b/base/src/main/java/bjc/utils/ioutils/format/CLParameters.java deleted file mode 100644 index bde7a7d..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/CLParameters.java +++ /dev/null @@ -1,224 +0,0 @@ -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; - - /** - * 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; - } - - /** - * 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 unsplit, Tape dirParams) { - List lParams = new ArrayList<>(); - StringBuilder currParm = new StringBuilder(); - - char prevChar = ' '; - - for (int i = 0; i < unsplit.length(); i++) { - char c = unsplit.charAt(i); - - if (c == ',' && prevChar != '\'') { - lParams.add(currParm.toString()); - - currParm = new StringBuilder(); - } else { - currParm.append(c); - } - - prevChar = c; - } - lParams.add(currParm.toString()); - - List parameters = new ArrayList<>(); - - if (lParams.size() == 1 && lParams.get(0).equals("")) - return new CLParameters(parameters.toArray(new String[0])); - - for(String param : lParams) { - 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.size() - dirParams.position())); - } else if (param.equals("%")) { - parameters.add(Integer.toString(dirParams.position())); - } else { - parameters.add(param); - } - } - - 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("")) { - 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.length() == 1) { - // Punt in the case we have a slightly malformed issue - return param.charAt(0); - } - - 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("")) { - 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) { - String msg = String.format("Invalid %s \"%s\" to %c directive", paramName, param, directive); - - IllegalArgumentException iaex = new IllegalArgumentException(msg); - iaex.initCause(nfex); - - throw iaex; - } - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("["); - - for (int i = 0; i < params.length; i++) { - if (i != 0) sb.append(", "); - - sb.append("\""); - sb.append(params[i]); - sb.append("\""); - } - - sb.append("]"); - - return sb.toString(); - } -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/EscapeException.java b/base/src/main/java/bjc/utils/ioutils/format/EscapeException.java deleted file mode 100644 index 086f1cd..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/EscapeException.java +++ /dev/null @@ -1,33 +0,0 @@ -package bjc.utils.ioutils.format; - -/** - * An exception thrown to escape CL iteration directives. - * - * @author EVE - * - */ -public class EscapeException extends RuntimeException { - private static final long serialVersionUID = -4552821131068559005L; - - /** - * Whether or not this exception should end iteration. - */ - public final boolean endIteration; - - /** - * Create a new escape exception. - */ - public EscapeException() { - endIteration = false; - } - - /** - * Create a new escape exception. - * - * @param end - * Whether or not to end the iteration. - */ - public EscapeException(boolean end) { - endIteration = end; - } -} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/AestheticDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/AestheticDirective.java deleted file mode 100644 index 9bad6d7..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/AestheticDirective.java +++ /dev/null @@ -1,74 +0,0 @@ -package bjc.utils.ioutils.format.directives; - -import java.io.IOException; - -import java.util.regex.Matcher; - -import bjc.utils.esodata.Tape; -import bjc.utils.ioutils.format.CLFormatter; -import bjc.utils.ioutils.format.CLModifiers; -import bjc.utils.ioutils.format.CLParameters; -import bjc.utils.ioutils.ReportWriter; - -/** - * Implementation of the A directive. - * - * @author student - * - */ -public class AestheticDirective implements Directive { - - @Override - public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape tParams, - Matcher dirMatcher, CLFormatter fmt) throws IOException { - // System.err.printf("Aesthetic directive with item \"%s\" and params: %s\n", item, tParams); - CLFormatter.checkItem(item, 'A'); - - int mincol = 0, colinc = 1, minpad = 0; - char padchar = ' '; - - if (params.length() == 0) { - // Zero parameters, use all defaults - } else if (params.length() == 1) { - mincol = params.getIntDefault(0, "minimum column count", 'A', 0); - } else if (params.length() < 4) { - throw new IllegalArgumentException("Must provide either zero, one or four arguments to A directive"); - } else { - 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); - } - } - } - - rw.write(work.toString()); - - tParams.right(); - } -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/CaseDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/CaseDirective.java deleted file mode 100644 index 728bb43..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/CaseDirective.java +++ /dev/null @@ -1,117 +0,0 @@ - -package bjc.utils.ioutils.format.directives; - -import bjc.utils.esodata.Tape; -import bjc.utils.ioutils.format.*; -import bjc.utils.ioutils.ReportWriter; - -import java.io.IOException; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.IllegalFormatConversionException; -import java.util.List; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class CaseDirective implements Directive { - private static final Pattern wordPattern = Pattern.compile("(\\w+)(\\b*)"); - - @Override - public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape tParams, - Matcher dirMatcher, CLFormatter fmt) throws IOException { - StringBuffer condBody = new StringBuffer(); - - int nestLevel = 1; - - 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("(")) { - if (nestLevel > 0) { - condBody.append(dirMatcher.group()); - } - - nestLevel += 1; - } else if (Directive.isOpening(dirName)) { - nestLevel += 1; - - condBody.append(dirMatcher.group()); - } else if (dirName.equals(")")) { - nestLevel = Math.max(0, nestLevel - 1); - - /* End the iteration. */ - if (nestLevel == 0) break; - } else if (Directive.isClosing(dirName)) { - nestLevel = Math.max(0, nestLevel - 1); - } else { - /* Not a special directive. */ - condBody.append(dirMatcher.group()); - } - } - } - - String frmt = condBody.toString(); - - ReportWriter nrw = rw.duplicate(new StringWriter()); - - fmt.doFormatString(frmt, nrw, tParams, false); - - String strang = nrw.toString(); - - if (mods.colonMod && mods.atMod) { - strang = strang.toUpperCase(); - } else if (mods.colonMod) { - Matcher mat = wordPattern.matcher(strang); - - StringBuffer sb = new StringBuffer(); - while(!mat.find()) { - mat.appendReplacement(sb, ""); - - String word = mat.group(1); - - word = word.substring(0, 1).toUpperCase() + word.substring(1); - - sb.append(word); - sb.append(mat.group(2)); - } - - mat.appendTail(sb); - - strang = sb.toString(); - } else if (mods.atMod) { - Matcher mat = wordPattern.matcher(strang); - - StringBuffer sb = new StringBuffer(); - boolean doCap = true; - while(!mat.find()) { - mat.appendReplacement(sb, ""); - - String word = mat.group(1); - - if (doCap) { - doCap = false; - - word = word.substring(0, 1).toUpperCase() + word.substring(1); - } - - sb.append(word); - sb.append(mat.group(2)); - } - - mat.appendTail(sb); - - strang = sb.toString(); - - } else { - strang = strang.toLowerCase(); - } - - rw.write(strang); - } -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/CharacterDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/CharacterDirective.java deleted file mode 100644 index 899e8e8..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/CharacterDirective.java +++ /dev/null @@ -1,45 +0,0 @@ -package bjc.utils.ioutils.format.directives; - -import bjc.utils.esodata.Tape; -import bjc.utils.ioutils.format.CLFormatter; -import bjc.utils.ioutils.format.CLModifiers; -import bjc.utils.ioutils.format.CLParameters; -import bjc.utils.ioutils.ReportWriter; - -import java.io.IOException; -import java.util.IllegalFormatConversionException; -import java.util.regex.Matcher; - -/** - * Implements the C directive. - * - * @author student - * - */ -public class CharacterDirective implements Directive { - - @Override - public void format(ReportWriter rw, Object parm, CLModifiers mods, CLParameters arrParams, Tape tParams, - Matcher dirMatcher, CLFormatter fmt) throws IOException { - 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. - */ - rw.write(Character.getName(codepoint)); - } else { - rw.write(ch); - } - - tParams.right(); - } - -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/ConditionalDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/ConditionalDirective.java deleted file mode 100644 index ed0b39b..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/ConditionalDirective.java +++ /dev/null @@ -1,193 +0,0 @@ -package bjc.utils.ioutils.format.directives; - -import bjc.utils.esodata.Tape; -import bjc.utils.ioutils.format.*; -import bjc.utils.ioutils.ReportWriter; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.IllegalFormatConversionException; -import java.util.List; -import java.util.logging.Logger; -import java.util.regex.Matcher; - -/** - * Implements the [ directive. - * - * @author student - * - */ -public class ConditionalDirective implements Directive { - private static Logger LOG = Logger.getLogger(ConditionalDirective.class.getName()); - - @Override - public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters arrParams, - Tape formatParams, Matcher dirMatcher, CLFormatter fmt) throws IOException { - StringBuffer condBody = new StringBuffer(); - - List clauses = new ArrayList<>(); - - String defClause = null; - boolean isDefault = false; - - int nestLevel = 1; - - while (dirMatcher.find()) { - /* Process a list of clauses. */ - String dirName = dirMatcher.group("name"); - String dirMods = dirMatcher.group("modifiers"); - - //System.err.printf("Found conditional directive %s with %s mods and level %d\n", dirName, dirMods, nestLevel); - if (dirName != null) { - /* Append everything up to this directive. */ - dirMatcher.appendReplacement(condBody, ""); - - if (dirName.equals("[")) { - if (nestLevel > 0) { - condBody.append(dirMatcher.group()); - } - nestLevel += 1; - } else if (Directive.isOpening(dirName)) { - nestLevel += 1; - - condBody.append(dirMatcher.group()); - } else if (dirName.equals("]")) { - nestLevel = Math.max(0, nestLevel - 1); - - if (nestLevel == 0) { - /* End the conditional. */ - String clause = condBody.toString(); - // System.err.printf("Found clause \"%s]\"\n", clause); - condBody = new StringBuffer(); - - if (isDefault) { - defClause = clause; - } - clauses.add(clause); - - break; - } else { - /* Not a special directive. */ - condBody.append(dirMatcher.group()); - } - } else if (Directive.isClosing(dirName)) { - nestLevel = Math.max(0, nestLevel - 1); - - condBody.append(dirMatcher.group()); - } else if (dirName.equals(";")) { - if (nestLevel == 1) { - /* End the clause. */ - String clause = condBody.toString(); - // System.err.printf("Found clause \"%s;\"\n", clause); - condBody = new StringBuffer(); - - if (isDefault) { - defClause = clause; - } - clauses.add(clause); - - /* - * Mark the next clause as the default. - */ - if (dirMods.contains(":")) { - isDefault = true; - } - } else { - /* Not a special directive. */ - condBody.append(dirMatcher.group()); - } - } else { - /* Not a special directive. */ - condBody.append(dirMatcher.group()); - } - } - } - - if (mods.starMod && clauses.size() > 0) defClause = clauses.get(0); - - try { - if (mods.colonMod) { - formatParams.right(); - - boolean res = false; - if (item == null) { - //throw new IllegalArgumentException("No parameter provided for [ directive."); - } else if (!(item instanceof Boolean)) { - throw new IllegalFormatConversionException('[', item.getClass()); - } else { - res = (Boolean) item; - } - - String frmt; - if (res) - frmt = clauses.get(1); - else - frmt = clauses.get(0); - - fmt.doFormatString(frmt, rw, formatParams, false); - } else if (mods.atMod) { - boolean res = false; - if (item == null) { - // throw new IllegalArgumentException("No parameter provided for [ directive."); - } else if (item instanceof Integer) { - if ((Integer)item != 0) res = true; - } else if (item instanceof Boolean) { - res = (Boolean) item; - } else { - throw new IllegalFormatConversionException('[', item.getClass()); - } - - if (res) { - fmt.doFormatString(clauses.get(0), rw, formatParams, false); - } else { - formatParams.right(); - } - } else { - int res; - if (arrParams.length() >= 1) { - res = arrParams.getInt(0, "conditional choice", '['); - } else { - if (item == null) { - throw new IllegalArgumentException("No parameter provided for [ directive."); - } else if (!(item instanceof Number)) { - throw new IllegalFormatConversionException('[', item.getClass()); - } - res = ((Number) item).intValue(); - - formatParams.right(); - } - - if (mods.dollarMod) res -= 1; - - // System.err.printf("Attempting selection of clause %d of %d (%s) (default %s)\n", - // res, clauses.size(), clauses, defClause); - if (clauses.size() == 0 || res < 0 || res >= clauses.size()) { - // System.err.printf("Selecting default clause (res %d, max %d): %s\n", res, clauses.size(), defClause); - // int clauseNo = 0; - // for (String clause : clauses) { - // System.err.printf("... clause %d: %s\n", ++clauseNo, clause); - // } - - if (defClause != null) fmt.doFormatString(defClause, rw, formatParams, false); - } else { - String frmt = clauses.get(res); - - // System.out.printf("Selecting clause %d of %d (params %s): %s\n", res, clauses.size(), formatParams, frmt); - fmt.doFormatString(frmt, rw, formatParams, false); - } - } - } catch (EscapeException eex) { - // @NOTE 9/5/18 - // - // I am not sure if it is valid to error here. I'm not - // even sure that we need to handle this here, but I - // dunno - //if (eex.endIteration) - // throw new UnsupportedOperationException("Colon mod not allowed on escape marker without colon mod on iteration"); - throw eex; - } - - return; - } - -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/Directive.java b/base/src/main/java/bjc/utils/ioutils/format/directives/Directive.java deleted file mode 100644 index 61abfc1..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/Directive.java +++ /dev/null @@ -1,72 +0,0 @@ -package bjc.utils.ioutils.format.directives; - -import java.io.IOException; -import java.util.regex.Matcher; - -import bjc.utils.esodata.Tape; -import bjc.utils.ioutils.format.CLFormatter; -import bjc.utils.ioutils.format.CLModifiers; -import bjc.utils.ioutils.format.CLParameters; -import bjc.utils.ioutils.ReportWriter; - -/** - * A CL format directive. - * - * @author EVE - * - */ -@FunctionalInterface -public interface Directive { - /** - * Execute this format directive. - * - * @param sb - * The buffer the string is being output to. - * @param item - * The current parameter being passed - * @param mods - * The directive modifiers - * @param arrParams - * The prefix parameters to the directive - * @param tParams - * All of the provided format parameters - * @param dirMatcher - * The matcher for format directives - * @param fmt - * The formatter itself. - */ - public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters arrParams, Tape tParams, - Matcher dirMatcher, CLFormatter fmt) throws IOException; - - public static boolean isOpening(String str) { - return isOpening(str.charAt(0)); - } - - public static boolean isOpening(char dir) { - switch(dir) { - case '(': - case '<': - case '[': - case '{': - return true; - default: - return false; - } - } - - public static boolean isClosing(String str) { - return isClosing(str.charAt(0)); - } - - public static boolean isClosing(char dir) { - switch(dir) { - case ')': - case '>': - case ']': - case '}': - return true; - default: - return false; - } - } -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/EscapeDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/EscapeDirective.java deleted file mode 100644 index 74488ed..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/EscapeDirective.java +++ /dev/null @@ -1,59 +0,0 @@ -package bjc.utils.ioutils.format.directives; - -import bjc.utils.esodata.Tape; -import bjc.utils.ioutils.format.CLFormatter; -import bjc.utils.ioutils.format.CLModifiers; -import bjc.utils.ioutils.format.CLParameters; -import bjc.utils.ioutils.format.EscapeException; -import bjc.utils.ioutils.ReportWriter; - -import java.util.regex.Matcher; - -/** - * Implementation for the ^ directive. - * @author student - * - */ -public class EscapeDirective implements Directive { - - @Override - public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, - Tape formatParams, Matcher dirMatcher, CLFormatter fmt) { - boolean shouldExit; - - if (mods.dollarMod) formatParams.right(); - - switch(params.length()) { - case 0: - shouldExit = formatParams.atEnd(); - 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; - } - - if (mods.dollarMod) formatParams.left(); - - /* 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/directives/FreshlineDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/FreshlineDirective.java deleted file mode 100644 index 3c02bfc..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/FreshlineDirective.java +++ /dev/null @@ -1,34 +0,0 @@ -package bjc.utils.ioutils.format.directives; - -import bjc.utils.esodata.Tape; -import bjc.utils.ioutils.format.CLFormatter; -import bjc.utils.ioutils.format.CLModifiers; -import bjc.utils.ioutils.format.CLParameters; -import bjc.utils.ioutils.ReportWriter; - -import java.io.IOException; -import java.util.regex.Matcher; - -/** - * Implement the & directive. - * @author student - * - */ -public class FreshlineDirective implements Directive { - - @Override - public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape tParams, - Matcher dirMatcher, CLFormatter fmt) throws IOException { - int nTimes = 1; - - if(params.length() >= 1) { - nTimes = params.getInt(0, "occurance count", '&'); - } - - if(rw.isLastCharNL()) nTimes -= 1; - - for(int i = 0; i < nTimes; i++) { - rw.write("\n"); - } - } -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/GeneralNumberDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/GeneralNumberDirective.java deleted file mode 100644 index 3eb741e..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/GeneralNumberDirective.java +++ /dev/null @@ -1,55 +0,0 @@ -package bjc.utils.ioutils.format.directives; - -import bjc.utils.ioutils.format.CLModifiers; -import bjc.utils.ioutils.format.CLParameters; -import bjc.utils.ioutils.ReportWriter; -import bjc.utils.math.NumberUtils; - -import java.io.IOException; - -/** - * Implementation skeleton for number directives. - * - * @author student - * - */ -public abstract class GeneralNumberDirective implements Directive { - protected static void handleNumberDirective(ReportWriter rw, CLModifiers mods, CLParameters params, int argidx, - long val, int radix) throws IOException { - /* - * 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 + 4)) { - commaChar = params.getCharDefault((argidx + 3), "comma character", 'R', ','); - } - if (params.length() >= (argidx + 5)) { - 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); - } - - rw.write(res); - } -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/GotoDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/GotoDirective.java deleted file mode 100644 index 7e30eab..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/GotoDirective.java +++ /dev/null @@ -1,47 +0,0 @@ -package bjc.utils.ioutils.format.directives; - -import bjc.utils.esodata.Tape; -import bjc.utils.ioutils.format.CLFormatter; -import bjc.utils.ioutils.format.CLModifiers; -import bjc.utils.ioutils.format.CLParameters; -import bjc.utils.ioutils.ReportWriter; - -import java.util.regex.Matcher; - -/** - * Implement the * directive. - * - * @author student - * - */ -public class GotoDirective implements Directive { - - @Override - public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape 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/directives/IterationDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/IterationDirective.java deleted file mode 100644 index 2ce6309..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/IterationDirective.java +++ /dev/null @@ -1,171 +0,0 @@ -package bjc.utils.ioutils.format.directives; - -import bjc.utils.esodata.SingleTape; -import bjc.utils.esodata.Tape; -import bjc.utils.ioutils.format.*; -import bjc.utils.ioutils.ReportWriter; - -import java.io.IOException; - -import java.util.Iterator; - -import java.util.IllegalFormatConversionException; -import java.util.regex.Matcher; - -/** - * Implements the { directive. - * - * @author student - * - */ -public class IterationDirective implements Directive { - - @Override - public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters arrParams, Tape tParams, - Matcher dirMatcher, CLFormatter fmt) throws IOException { - 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("}")) { - break; - } else { - /* Not a special directive. */ - condBody.append(dirMatcher.group()); - } - } - } - - String frmt = condBody.toString(); - Object iter = item; - - // System.err.printf("Iteration format \"%s\" (iter %s)\n", frmt, 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", '{'); - } - - int numItr = 0; - - if (mods.atMod && mods.colonMod) { - try { - do { - if (numItr > maxItr) break; - numItr += 1; - - if (!(iter instanceof Iterable)) { - throw new IllegalFormatConversionException('{', iter.getClass()); - } - - @SuppressWarnings("unchecked") - Iterable nitr = (Iterable) iter; - Tape nParams = new SingleTape<>(nitr); - - try { - fmt.doFormatString(frmt, rw, nParams, false); - } catch (EscapeException eex) { - if (eex.endIteration) { - if (tParams.atEnd()) { - throw eex; - } - } - } - - tParams.right(); - iter = tParams.item(); - } while (tParams.position() < tParams.size()); - } catch (EscapeException eex) { - } - } else if (mods.atMod) { - try { - while (!tParams.atEnd()) { - // System.err.printf("Iterating with format \"%s\"\n", frmt); - if (numItr > maxItr) break; - numItr += 1; - - fmt.doFormatString(frmt, rw, tParams, false); - } - } catch (EscapeException eex) { - if (eex.endIteration) - throw new UnsupportedOperationException("Colon mod not allowed on escape marker without colon mod on iteration"); - } - } else if (mods.colonMod) { - if (!(item instanceof Iterable)) { - throw new IllegalFormatConversionException('{', item.getClass()); - } - - try { - @SuppressWarnings("unchecked") - Iterable itb = (Iterable) item; - Iterator itr = itb.iterator(); - while (itr.hasNext()) { - Object obj = itr.next(); - - if (numItr > maxItr) break; - numItr += 1; - - if (!(obj instanceof Iterable)) { - throw new IllegalFormatConversionException('{', obj.getClass()); - } - - @SuppressWarnings("unchecked") - Iterable nitr = (Iterable) obj; - Tape nParams = new SingleTape<>(nitr); - - try { - fmt.doFormatString(frmt, rw, nParams, false); - } catch (EscapeException eex) { - if(eex.endIteration && !itr.hasNext()) throw eex; - } - } - } catch (EscapeException eex) { - } - } else { - if (!(item instanceof Iterable)) { - throw new IllegalFormatConversionException('{', item.getClass()); - } - - try { - @SuppressWarnings("unchecked") - Iterable itr = (Iterable) item; - Tape nParams = new SingleTape<>(itr); - - while (!nParams.atEnd()) { - if (numItr > maxItr) break; - numItr += 1; - - fmt.doFormatString(frmt, rw, nParams, false); - } - } catch (EscapeException eex) { - if (eex.endIteration) - throw new UnsupportedOperationException("Colon mod not allowed on escape marker without colon mod on iteration"); - } - } - - tParams.right(); - } -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/LiteralDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/LiteralDirective.java deleted file mode 100644 index d833654..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/LiteralDirective.java +++ /dev/null @@ -1,51 +0,0 @@ -package bjc.utils.ioutils.format.directives; - -import bjc.utils.esodata.Tape; -import bjc.utils.ioutils.format.CLFormatter; -import bjc.utils.ioutils.format.CLModifiers; -import bjc.utils.ioutils.format.CLParameters; -import bjc.utils.ioutils.ReportWriter; - -import java.io.IOException; -import java.util.regex.Matcher; - -/** - * Implements directives that create a literal string. - * - * @author student - * - */ -public class LiteralDirective implements Directive { - - private char directive; - private String lit; - - /** - * Create a new literal directive. - * - * @param lit - * The string for the directive. - * @param directive - * The character for the directive. - */ - public LiteralDirective(String lit, char directive) { - this.directive = directive; - this.lit = lit; - } - - @Override - public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape tParams, - Matcher dirMatcher, CLFormatter fmt) throws IOException { - int nTimes = 1; - - if (params.length() >= 1) { - nTimes = params.getInt(0, "occurance count", directive); - } - - for (int i = 0; i < nTimes; i++) { - rw.write(lit); - } - - } - -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/NumberDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/NumberDirective.java deleted file mode 100644 index 88b3e7e..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/NumberDirective.java +++ /dev/null @@ -1,57 +0,0 @@ -package bjc.utils.ioutils.format.directives; - -import java.io.IOException; -import java.util.IllegalFormatConversionException; -import java.util.regex.Matcher; - -import bjc.utils.esodata.Tape; -import bjc.utils.ioutils.format.CLFormatter; -import bjc.utils.ioutils.format.CLModifiers; -import bjc.utils.ioutils.format.CLParameters; -import bjc.utils.ioutils.ReportWriter; - -/** - * Implements radix based numbers. - * - * @author student - * - */ -public class NumberDirective extends GeneralNumberDirective { - - /** - * Create a new radix based number directive. - * - * @param argidx - * The argument offset to use. - * @param radix - * The radix of the number to use. - */ - public NumberDirective(int argidx, int radix, char directive) { - this.argidx = argidx; - this.radix = radix; - - this.directive = directive; - } - - private int argidx; - private int radix; - - private char directive; - - @Override - public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape tParams, - Matcher dirMatcher, CLFormatter fmt) throws IOException { - CLFormatter.checkItem(item, directive); - - if (!(item instanceof Number)) { - throw new IllegalFormatConversionException(directive, item.getClass()); - } - - long val = ((Number) item).longValue(); - - handleNumberDirective(rw, mods, params, argidx, val, radix); - - tParams.right(); - } - -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/RadixDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/RadixDirective.java deleted file mode 100644 index e8dd4b0..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/RadixDirective.java +++ /dev/null @@ -1,55 +0,0 @@ -package bjc.utils.ioutils.format.directives; - -import bjc.utils.esodata.Tape; -import bjc.utils.ioutils.format.CLFormatter; -import bjc.utils.ioutils.format.CLModifiers; -import bjc.utils.ioutils.format.CLParameters; -import bjc.utils.ioutils.ReportWriter; -import bjc.utils.math.NumberUtils; - -import java.io.IOException; -import java.util.IllegalFormatConversionException; -import java.util.regex.Matcher; - -/** - * Generalized radix directive. - * - * @author student - * - */ -public class RadixDirective extends GeneralNumberDirective { - - @Override - public void format(ReportWriter rw, Object arg, CLModifiers mods, CLParameters params, Tape tParams, - Matcher dirMatcher, CLFormatter fmt) throws IOException { - 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) { - rw.write(NumberUtils.toRoman(val, mods.colonMod)); - } else if (mods.colonMod) { - rw.write(NumberUtils.toOrdinal(val)); - } else { - rw.write(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(rw, mods, params, 0, val, radix); - } - - tParams.right(); - } -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/RecursiveDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/RecursiveDirective.java deleted file mode 100644 index 44a25ad..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/RecursiveDirective.java +++ /dev/null @@ -1,49 +0,0 @@ -package bjc.utils.ioutils.format.directives; - -import bjc.utils.esodata.SingleTape; -import bjc.utils.esodata.Tape; -import bjc.utils.ioutils.format.*; -import bjc.utils.ioutils.ReportWriter; -import java.util.IllegalFormatConversionException; - -import java.io.IOException; -import java.util.regex.Matcher; - -public class RecursiveDirective implements Directive { - public void format(ReportWriter rw, Object arg, CLModifiers mods, CLParameters params, Tape tParams, - Matcher dirMatcher, CLFormatter fmt) throws IOException { - tParams.right(); - - CLFormatter.checkItem(arg, '?'); - - if (mods.atMod) { - if (!(arg instanceof String)) - throw new IllegalFormatConversionException('?', arg.getClass()); - - try { - fmt.doFormatString((String)arg, rw, tParams, true); - } catch (EscapeException eex) { - if (eex.endIteration) - throw new UnsupportedOperationException("Colon mod not allowed on escape marker without colon mod on iteration"); - } - } else { - if (tParams.atEnd()) - throw new IllegalArgumentException("? directive requires two format parameters"); - - Object o = tParams.item(); - tParams.right(); - - if (!(o instanceof Iterable)) - throw new IllegalFormatConversionException('?', o.getClass()); - - Iterable itb = (Iterable)o; - Tape newParams = new SingleTape<>(itb); - - try { - fmt.doFormatString((String)arg, rw, newParams, true); - } catch (EscapeException eex) { - throw new UnsupportedOperationException("Colon mod not allowed on escape marker without colon mod on iteration"); - } - } - } -} diff --git a/base/src/main/java/bjc/utils/ioutils/format/directives/TabulateDirective.java b/base/src/main/java/bjc/utils/ioutils/format/directives/TabulateDirective.java deleted file mode 100644 index d9136f2..0000000 --- a/base/src/main/java/bjc/utils/ioutils/format/directives/TabulateDirective.java +++ /dev/null @@ -1,80 +0,0 @@ -package bjc.utils.ioutils.format.directives; - -import java.io.IOException; -import java.util.regex.Matcher; - -import bjc.utils.esodata.Tape; - -import bjc.utils.ioutils.ReportWriter; -import bjc.utils.ioutils.format.*; - -public class TabulateDirective implements Directive { - public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters arrParams, Tape tParams, - Matcher dirMatcher, CLFormatter fmt) throws IOException { - // Unsupported feature. - // - // I can't really make out what this is supposed to do from the - // documentation, but I suspect that it depends on font glyph - // size, not character positions - if (mods.colonMod) { - throw new UnsupportedOperationException("Colon mod is not supported for T directive"); - } - - // Support for a possible future feature - char padchar = ' '; - - if (mods.atMod) { - int colrel = 1, colinc = 1; - - if (arrParams.length() > 2) { - colinc = arrParams.getIntDefault(1, "column increment", 'T', 1); - } - - if (arrParams.length() > 1) { - colrel = arrParams.getIntDefault(0, "relative column number", 'T', 1); - } - - for (int i = 0; i < colrel; i++) { - rw.write(padchar); - } - - int currCol = rw.getLinePos(); - - int nSpaces = 0; - - while ((currCol + nSpaces) % colinc != 0) nSpaces++; - - for (int i = 0; i < nSpaces; i++) { - rw.write(padchar); - } - } else { - int colnum = 1, colinc = 1; - - if (arrParams.length() > 2) { - colinc = arrParams.getIntDefault(1, "column increment", 'T', 1); - } - - if (arrParams.length() > 1) { - colnum = arrParams.getIntDefault(0, "column number", 'T', 1); - } - - int currCol = rw.getLinePos(); - - if (currCol < colnum) { - for (int i = currCol; i < colnum; i++) { - rw.write(padchar); - } - } else { - if (colinc == 0) return; - - int k = 0; - - while (colnum > (currCol + (k * colinc))) k++; - - for (int i = currCol; i < colnum; i++) { - rw.write(padchar); - } - } - } - } -} diff --git a/base/src/test/java/bjc/utils/test/ioutils/CLFormatterTest.java b/base/src/test/java/bjc/utils/test/ioutils/CLFormatterTest.java deleted file mode 100644 index 08dfe78..0000000 --- a/base/src/test/java/bjc/utils/test/ioutils/CLFormatterTest.java +++ /dev/null @@ -1,128 +0,0 @@ -package bjc.utils.test.ioutils; - -import static org.junit.Assert.*; - -import java.io.IOException; - -import java.util.Arrays; - -import bjc.utils.ioutils.format.CLFormatter; - -import org.junit.Test; - -import static java.util.Arrays.asList; -/** - * Tests for CL format strings. - * - * @author EVE - * - */ -@SuppressWarnings("javadoc") -public class CLFormatterTest { - private CLFormatter fmt = new CLFormatter(); - - @Test - public void testLiteralString() { - // Print literal strings exactly - assertEquals("foo", format("foo")); - } - - @Test - public void testDecimalPrinting() { - // Test decimal printing - assertEquals("5", format("~D", 5)); - assertEquals(" 5", format("~3D", 5)); - assertEquals("005", format("~3,'0D", 5)); - assertEquals("6|55|35", format("~,,'|,2:D", 0xFFFF)); - } - - @Test - public void testRadixPrinting() { - // Test radix printing - assertEquals("1 22", format("~3,,,' ,2:R", 17)); - } - - @Test - public void testBinaryPrinting() { - // Test binary printing - assertEquals("1101", format("~,,' ,4:B", 13)); - assertEquals("1 0001", format("~,,' ,4:B", 17)); - // @NOTE 9/6/18 :CommaPad - // - // I'm not sure how this is the expected behavior, unless the - // comma interval is enforced in the case of a digit padchar. - // assertEquals("0000 1101 0000 0101", format("~19,0,' ,4:B", 3333)); - assertEquals("000001101 0000 0101", format("~19,0,' ,4:B", 3333)); - } - - @Test - public void testConditionalPrinting() { - // Test conditional printing - assertEquals("print length = 5", format("~@[print level = ~D~]~@[print length = ~D~]", null, 5)); - } - - @Test - public void testIterationPrinting() { - assertEquals("The winners are: fred harry jill.", format("The winners are:~{ ~S~}.", - asList("fred", "harry", "jill"))); - assertEquals("Pairs: (1, 1) (2, 2) (3, 3).", format("Pairs:~{ (~S, ~S)~}.", - asList(1, 1, 2, 2, 3, 3))); - - assertEquals("Pairs: (1, 1) (2, 2) (3, 3).", format("Pairs:~:{ (~S, ~S)~}.", - asList(asList(1, 1), asList(2, 2), asList(3, 3)))); - - assertEquals("Pairs: (1, 1) (2, 2) (3, 3).", format("Pairs:~@{ (~S, ~S)~}.", - 1, 1, 2, 2, 3, 3)); - - assertEquals("Pairs: (1, 1) (2, 2) (3, 3).", format("Pairs:~:@{ (~S, ~S)~}.", - asList(1, 1), asList(2, 2), asList(3, 3))); - } - - @Test - public void testRecursivePrinting() { - assertEquals(" 7", format("~? ~D", "<~A ~D>", asList("Foo", 5), 7)); - assertEquals(" 7", format("~? ~D", "<~A ~D>", asList("Foo", 5, 14), 7)); - - assertEquals(" 7", format("~@? ~D", "<~A ~D>", "Foo", 5, 7)); - assertEquals(" 14", format("~@? ~D", "<~A ~D>", "Foo", 5, 14, 7)); - } - - @Test - public void testEscapePrinting() { - assertEquals("Done.", format("Done.~^ ~D warning.~^ ~D error.")); - assertEquals("Done. 3 warning.", format("Done.~^ ~D warning.~^ ~D error.", 3)); - assertEquals("Done. 1 warning. 5 error.", format("Done.~^ ~D warning.~^ ~D error.", 1, 5)); - } - - @Test - public void testCapsPrinting() { - assertEquals("XIV xiv", format("~@R ~(~@R~)", 14, 14)); - } - - @Test - public void testListPrinting() { - // Test printing a list - // String fmtStr = "Items:~#[ none~; ~A~; ~A and ~A~:;~@{~#[~; and~] ~A~^,~}~]."; - String fmtStr = "Items:~#[ none~; ~A~; ~A and ~A~:;~@{~#*[ ~A,~; and ~A~; ~A~]~}~]."; - - assertEquals("Items: none.", format(fmtStr)); - assertEquals("Items: foo.", format(fmtStr, "foo")); - assertEquals("Items: foo and bar.", format(fmtStr, "foo", "bar")); - assertEquals("Items: foo, bar and baz.", format(fmtStr, "foo", "bar", "baz")); - assertEquals("Items: foo, bar, baz and quux.", format(fmtStr, "foo", "bar", "baz", "quux")); - } - - @Test - public void testRandomCases() { - // Random test cases - assertEquals("3 dogs are here", format("~D dog~:[s are~; is~] here", 3, 3 == 1)); - } - - private String format(String str, Object... params) { - try { - return fmt.formatString(str, params); - } catch (IOException ioex) { - return null; - } - } -} diff --git a/clformat/.gitignore b/clformat/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/clformat/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/clformat/pom.xml b/clformat/pom.xml new file mode 100644 index 0000000..23ddb58 --- /dev/null +++ b/clformat/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + BJCUtils-Parent + bjc + 1.0.0 + + + clformat + jar + + + UTF-8 + + + clformat + + + + junit + junit + 4.12 + test + + + bjc + BJC-Utils2 + 1.0.0 + + + + bjc + inflexion + 1.0.0 + + + + + + + data/ + + + **/*.txt + **/*.sprop + + + + + + + maven-compiler-plugin + 3.7.0 + + + 1.8 + 1.8 + + + + maven-clean-plugin + 3.0.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-surefire-plugin + 2.20.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + + diff --git a/clformat/src/main/java/bjc/App.java b/clformat/src/main/java/bjc/App.java new file mode 100644 index 0000000..ff1c4f9 --- /dev/null +++ b/clformat/src/main/java/bjc/App.java @@ -0,0 +1,13 @@ +package bjc; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/CLFormatter.java b/clformat/src/main/java/bjc/utils/ioutils/format/CLFormatter.java new file mode 100644 index 0000000..9e01ca6 --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/CLFormatter.java @@ -0,0 +1,282 @@ +package bjc.utils.ioutils.format; + +import bjc.utils.esodata.SingleTape; +import bjc.utils.esodata.Tape; +import bjc.utils.ioutils.format.directives.*; +import bjc.utils.ioutils.ReportWriter; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; + +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.misc.PropertyDB.applyFormat; +import static bjc.utils.misc.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 directiveName = getRegex("clFormatName"); + + private static final String prefixList = applyFormat("delimSeparatedList", prefixParam, ","); + 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()); + // @NOTE 9/6/18 + // + // This is just an alias, not the actual S directive + builtinDirectives.put("S", new AestheticDirective()); + + builtinDirectives.put("C", new CharacterDirective()); + + builtinDirectives.put("B", new NumberDirective(-1, 2, 'B')); + builtinDirectives.put("O", new NumberDirective(-1, 8, 'O')); + builtinDirectives.put("D", new NumberDirective(-1, 10, 'D')); + builtinDirectives.put("X", new NumberDirective(-1, 16, 'X')); + + 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 RecursiveDirective()); + + builtinDirectives.put("*", new GotoDirective()); + + builtinDirectives.put("^", new EscapeDirective()); + builtinDirectives.put("[", new ConditionalDirective()); + builtinDirectives.put("{", new IterationDirective()); + builtinDirectives.put("(", new CaseDirective()); + + builtinDirectives.put("T", new TabulateDirective()); + } + + /** + * Create a new CL formatter. + */ + public CLFormatter() { + extraDirectives = new HashMap<>(); + } + + /** + * Check that an item is valid for a directive. + * + * @param itm + * The item to check. + * @param directive + * The directive to check for. + */ + public 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) throws IOException { + ReportWriter rw = new ReportWriter(new StringWriter()); + /* Put the parameters where we can easily handle them. */ + Tape tParams = new SingleTape<>(params); + + doFormatString(format, rw, tParams, true); + + return rw.toString(); + } + + /** + * 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, Iterable params) throws IOException { + ReportWriter rw = new ReportWriter(new StringWriter()); + + /* Put the parameters where we can easily handle them. */ + Tape tParams = new SingleTape<>(params); + + doFormatString(format, rw, tParams, true); + + return rw.toString(); + } + + /** + * Format a string in the style of CL's FORMAT. + * + * @param format + * The format string to use. + * @param params + * The parameters for the string. + */ + public void formatString(Writer target, String format, Object... params) throws IOException { + ReportWriter rw = new ReportWriter(target); + /* Put the parameters where we can easily handle them. */ + Tape tParams = new SingleTape<>(params); + + doFormatString(format, rw, tParams, true); + } + + /** + * 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 void formatString(Writer target, String format, Iterable params) throws IOException { + ReportWriter rw = new ReportWriter(target); + + /* Put the parameters where we can easily handle them. */ + Tape tParams = new SingleTape<>(params); + + doFormatString(format, rw, tParams, true); + } + + /** + * Fill in a partially started format string. + * + * Used mostly for directives that require formatting again with a + * different string. + * + * @param format + * The format to use. + * @param sb + * The buffer to file output into. + * @param tParams + * The parameters to use. + */ + public void doFormatString(String format, ReportWriter rw, Tape tParams, boolean isToplevel) throws IOException { + Matcher dirMatcher = pFormatDirective.matcher(format); + + // We need this StringBuffer to use appendReplacement and stuff + // from Matcher. The fact that for some reason, StringBuffer is + // final prevents us from using our own dummy StringBuffer that + // auto-flushes to our output stream, so we have to do it + // ourselves. + StringBuffer sb = new StringBuffer(); + + boolean doTail = true; + try { + while(dirMatcher.find()) { + dirMatcher.appendReplacement(sb, ""); + rw.writeBuffer(sb); + + String dirName = dirMatcher.group("name"); + String dirFunc = dirMatcher.group("funcname"); + String dirMods = dirMatcher.group("modifiers"); + String dirParams = dirMatcher.group("params"); + + if(dirMods == null) dirMods = ""; + if(dirParams == null) dirParams = ""; + + CLParameters arrParams = CLParameters.fromDirective(dirParams, 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(rw, item, mods, arrParams, tParams, dirMatcher, + this); + + continue; + } + + if(builtinDirectives.containsKey(dirName)) { + // System.err.printf("Executing directive %s (%s) (%d to %d) from string %s\n", dirName, dirMatcher.group(), dirMatcher.start(), dirMatcher.end(), format); + + builtinDirectives.get(dirName).format(rw, + item, mods, arrParams, tParams, dirMatcher, this); + + continue; + } + + if(dirName == null) dirName = ""; + + switch(dirName) { + case "]": + throw new IllegalArgumentException("Found conditional-end outside of conditional."); + case ";": + throw new IllegalArgumentException( + "Found seperator outside of block."); + case "}": + throw new IllegalArgumentException("Found iteration-end outside of iteration"); + case "<": + case ">": + 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 "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"); + case "P": + throw new IllegalArgumentException("These directives aren't implemented yet"); + case ")": + throw new IllegalArgumentException("Case-conversion end outside of case conversion"); + case "\n": + /* + * Ignored newline. + */ + break; + default: + String msg = String.format("Unknown format directive '%s'", dirName); + throw new IllegalArgumentException(msg); + } + } + } catch (EscapeException eex) { + if (!isToplevel) throw eex; + + doTail = false; + } + + if (doTail) dirMatcher.appendTail(sb); + rw.writeBuffer(sb); + } +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/CLModifiers.java b/clformat/src/main/java/bjc/utils/ioutils/format/CLModifiers.java new file mode 100644 index 0000000..68127b6 --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/CLModifiers.java @@ -0,0 +1,66 @@ +package bjc.utils.ioutils.format; + +/** + * A collection of the modifiers attached to a CL format directive. + * + * @author EVE + * + */ +public class CLModifiers { + /** + * Whether the at mod is on. + */ + public final boolean atMod; + /** + * Whether the colon mod is on. + */ + public final boolean colonMod; + /** + * Whether the dollar mod is on. + */ + public final boolean dollarMod; + /** + * Whether the star mod is on. + */ + public final boolean starMod; + + /** + * Create a new set of CL modifiers. + * + * @param at + * The state of the at mod. + * @param colon + * The state of the colon mod. + * @param dollar + * The state of the dollar mod. + */ + public CLModifiers(boolean at, boolean colon, boolean dollar, boolean star) { + atMod = at; + colonMod = colon; + dollarMod = dollar; + starMod = star; + } + + /** + * Create a set of modifiers from a modifier string. + * + * @param modString + * The string to parse modifiers from. + * @return A set of modifiers matching the string. + */ + public static CLModifiers fromString(String modString) { + boolean atMod = false; + boolean colonMod = false; + boolean dollarMod = false; + boolean starMod = false; + + if(modString != null) { + atMod = modString.contains("@"); + colonMod = modString.contains(":"); + dollarMod = modString.contains("$"); + starMod = modString.contains("*"); + } + + return new CLModifiers(atMod, colonMod, dollarMod, starMod); + } +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/CLParameters.java b/clformat/src/main/java/bjc/utils/ioutils/format/CLParameters.java new file mode 100644 index 0000000..bde7a7d --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/CLParameters.java @@ -0,0 +1,224 @@ +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; + + /** + * 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; + } + + /** + * 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 unsplit, Tape dirParams) { + List lParams = new ArrayList<>(); + StringBuilder currParm = new StringBuilder(); + + char prevChar = ' '; + + for (int i = 0; i < unsplit.length(); i++) { + char c = unsplit.charAt(i); + + if (c == ',' && prevChar != '\'') { + lParams.add(currParm.toString()); + + currParm = new StringBuilder(); + } else { + currParm.append(c); + } + + prevChar = c; + } + lParams.add(currParm.toString()); + + List parameters = new ArrayList<>(); + + if (lParams.size() == 1 && lParams.get(0).equals("")) + return new CLParameters(parameters.toArray(new String[0])); + + for(String param : lParams) { + 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.size() - dirParams.position())); + } else if (param.equals("%")) { + parameters.add(Integer.toString(dirParams.position())); + } else { + parameters.add(param); + } + } + + 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("")) { + 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.length() == 1) { + // Punt in the case we have a slightly malformed issue + return param.charAt(0); + } + + 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("")) { + 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) { + String msg = String.format("Invalid %s \"%s\" to %c directive", paramName, param, directive); + + IllegalArgumentException iaex = new IllegalArgumentException(msg); + iaex.initCause(nfex); + + throw iaex; + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("["); + + for (int i = 0; i < params.length; i++) { + if (i != 0) sb.append(", "); + + sb.append("\""); + sb.append(params[i]); + sb.append("\""); + } + + sb.append("]"); + + return sb.toString(); + } +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/EscapeException.java b/clformat/src/main/java/bjc/utils/ioutils/format/EscapeException.java new file mode 100644 index 0000000..086f1cd --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/EscapeException.java @@ -0,0 +1,33 @@ +package bjc.utils.ioutils.format; + +/** + * An exception thrown to escape CL iteration directives. + * + * @author EVE + * + */ +public class EscapeException extends RuntimeException { + private static final long serialVersionUID = -4552821131068559005L; + + /** + * Whether or not this exception should end iteration. + */ + public final boolean endIteration; + + /** + * Create a new escape exception. + */ + public EscapeException() { + endIteration = false; + } + + /** + * Create a new escape exception. + * + * @param end + * Whether or not to end the iteration. + */ + public EscapeException(boolean end) { + endIteration = end; + } +} \ No newline at end of file diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/directives/AestheticDirective.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/AestheticDirective.java new file mode 100644 index 0000000..9bad6d7 --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/AestheticDirective.java @@ -0,0 +1,74 @@ +package bjc.utils.ioutils.format.directives; + +import java.io.IOException; + +import java.util.regex.Matcher; + +import bjc.utils.esodata.Tape; +import bjc.utils.ioutils.format.CLFormatter; +import bjc.utils.ioutils.format.CLModifiers; +import bjc.utils.ioutils.format.CLParameters; +import bjc.utils.ioutils.ReportWriter; + +/** + * Implementation of the A directive. + * + * @author student + * + */ +public class AestheticDirective implements Directive { + + @Override + public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape tParams, + Matcher dirMatcher, CLFormatter fmt) throws IOException { + // System.err.printf("Aesthetic directive with item \"%s\" and params: %s\n", item, tParams); + CLFormatter.checkItem(item, 'A'); + + int mincol = 0, colinc = 1, minpad = 0; + char padchar = ' '; + + if (params.length() == 0) { + // Zero parameters, use all defaults + } else if (params.length() == 1) { + mincol = params.getIntDefault(0, "minimum column count", 'A', 0); + } else if (params.length() < 4) { + throw new IllegalArgumentException("Must provide either zero, one or four arguments to A directive"); + } else { + 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); + } + } + } + + rw.write(work.toString()); + + tParams.right(); + } +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/directives/CaseDirective.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/CaseDirective.java new file mode 100644 index 0000000..728bb43 --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/CaseDirective.java @@ -0,0 +1,117 @@ + +package bjc.utils.ioutils.format.directives; + +import bjc.utils.esodata.Tape; +import bjc.utils.ioutils.format.*; +import bjc.utils.ioutils.ReportWriter; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.IllegalFormatConversionException; +import java.util.List; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class CaseDirective implements Directive { + private static final Pattern wordPattern = Pattern.compile("(\\w+)(\\b*)"); + + @Override + public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape tParams, + Matcher dirMatcher, CLFormatter fmt) throws IOException { + StringBuffer condBody = new StringBuffer(); + + int nestLevel = 1; + + 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("(")) { + if (nestLevel > 0) { + condBody.append(dirMatcher.group()); + } + + nestLevel += 1; + } else if (Directive.isOpening(dirName)) { + nestLevel += 1; + + condBody.append(dirMatcher.group()); + } else if (dirName.equals(")")) { + nestLevel = Math.max(0, nestLevel - 1); + + /* End the iteration. */ + if (nestLevel == 0) break; + } else if (Directive.isClosing(dirName)) { + nestLevel = Math.max(0, nestLevel - 1); + } else { + /* Not a special directive. */ + condBody.append(dirMatcher.group()); + } + } + } + + String frmt = condBody.toString(); + + ReportWriter nrw = rw.duplicate(new StringWriter()); + + fmt.doFormatString(frmt, nrw, tParams, false); + + String strang = nrw.toString(); + + if (mods.colonMod && mods.atMod) { + strang = strang.toUpperCase(); + } else if (mods.colonMod) { + Matcher mat = wordPattern.matcher(strang); + + StringBuffer sb = new StringBuffer(); + while(!mat.find()) { + mat.appendReplacement(sb, ""); + + String word = mat.group(1); + + word = word.substring(0, 1).toUpperCase() + word.substring(1); + + sb.append(word); + sb.append(mat.group(2)); + } + + mat.appendTail(sb); + + strang = sb.toString(); + } else if (mods.atMod) { + Matcher mat = wordPattern.matcher(strang); + + StringBuffer sb = new StringBuffer(); + boolean doCap = true; + while(!mat.find()) { + mat.appendReplacement(sb, ""); + + String word = mat.group(1); + + if (doCap) { + doCap = false; + + word = word.substring(0, 1).toUpperCase() + word.substring(1); + } + + sb.append(word); + sb.append(mat.group(2)); + } + + mat.appendTail(sb); + + strang = sb.toString(); + + } else { + strang = strang.toLowerCase(); + } + + rw.write(strang); + } +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/directives/CharacterDirective.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/CharacterDirective.java new file mode 100644 index 0000000..899e8e8 --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/CharacterDirective.java @@ -0,0 +1,45 @@ +package bjc.utils.ioutils.format.directives; + +import bjc.utils.esodata.Tape; +import bjc.utils.ioutils.format.CLFormatter; +import bjc.utils.ioutils.format.CLModifiers; +import bjc.utils.ioutils.format.CLParameters; +import bjc.utils.ioutils.ReportWriter; + +import java.io.IOException; +import java.util.IllegalFormatConversionException; +import java.util.regex.Matcher; + +/** + * Implements the C directive. + * + * @author student + * + */ +public class CharacterDirective implements Directive { + + @Override + public void format(ReportWriter rw, Object parm, CLModifiers mods, CLParameters arrParams, Tape tParams, + Matcher dirMatcher, CLFormatter fmt) throws IOException { + 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. + */ + rw.write(Character.getName(codepoint)); + } else { + rw.write(ch); + } + + tParams.right(); + } + +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/directives/ConditionalDirective.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/ConditionalDirective.java new file mode 100644 index 0000000..ed0b39b --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/ConditionalDirective.java @@ -0,0 +1,193 @@ +package bjc.utils.ioutils.format.directives; + +import bjc.utils.esodata.Tape; +import bjc.utils.ioutils.format.*; +import bjc.utils.ioutils.ReportWriter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.IllegalFormatConversionException; +import java.util.List; +import java.util.logging.Logger; +import java.util.regex.Matcher; + +/** + * Implements the [ directive. + * + * @author student + * + */ +public class ConditionalDirective implements Directive { + private static Logger LOG = Logger.getLogger(ConditionalDirective.class.getName()); + + @Override + public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters arrParams, + Tape formatParams, Matcher dirMatcher, CLFormatter fmt) throws IOException { + StringBuffer condBody = new StringBuffer(); + + List clauses = new ArrayList<>(); + + String defClause = null; + boolean isDefault = false; + + int nestLevel = 1; + + while (dirMatcher.find()) { + /* Process a list of clauses. */ + String dirName = dirMatcher.group("name"); + String dirMods = dirMatcher.group("modifiers"); + + //System.err.printf("Found conditional directive %s with %s mods and level %d\n", dirName, dirMods, nestLevel); + if (dirName != null) { + /* Append everything up to this directive. */ + dirMatcher.appendReplacement(condBody, ""); + + if (dirName.equals("[")) { + if (nestLevel > 0) { + condBody.append(dirMatcher.group()); + } + nestLevel += 1; + } else if (Directive.isOpening(dirName)) { + nestLevel += 1; + + condBody.append(dirMatcher.group()); + } else if (dirName.equals("]")) { + nestLevel = Math.max(0, nestLevel - 1); + + if (nestLevel == 0) { + /* End the conditional. */ + String clause = condBody.toString(); + // System.err.printf("Found clause \"%s]\"\n", clause); + condBody = new StringBuffer(); + + if (isDefault) { + defClause = clause; + } + clauses.add(clause); + + break; + } else { + /* Not a special directive. */ + condBody.append(dirMatcher.group()); + } + } else if (Directive.isClosing(dirName)) { + nestLevel = Math.max(0, nestLevel - 1); + + condBody.append(dirMatcher.group()); + } else if (dirName.equals(";")) { + if (nestLevel == 1) { + /* End the clause. */ + String clause = condBody.toString(); + // System.err.printf("Found clause \"%s;\"\n", clause); + condBody = new StringBuffer(); + + if (isDefault) { + defClause = clause; + } + clauses.add(clause); + + /* + * Mark the next clause as the default. + */ + if (dirMods.contains(":")) { + isDefault = true; + } + } else { + /* Not a special directive. */ + condBody.append(dirMatcher.group()); + } + } else { + /* Not a special directive. */ + condBody.append(dirMatcher.group()); + } + } + } + + if (mods.starMod && clauses.size() > 0) defClause = clauses.get(0); + + try { + if (mods.colonMod) { + formatParams.right(); + + boolean res = false; + if (item == null) { + //throw new IllegalArgumentException("No parameter provided for [ directive."); + } else if (!(item instanceof Boolean)) { + throw new IllegalFormatConversionException('[', item.getClass()); + } else { + res = (Boolean) item; + } + + String frmt; + if (res) + frmt = clauses.get(1); + else + frmt = clauses.get(0); + + fmt.doFormatString(frmt, rw, formatParams, false); + } else if (mods.atMod) { + boolean res = false; + if (item == null) { + // throw new IllegalArgumentException("No parameter provided for [ directive."); + } else if (item instanceof Integer) { + if ((Integer)item != 0) res = true; + } else if (item instanceof Boolean) { + res = (Boolean) item; + } else { + throw new IllegalFormatConversionException('[', item.getClass()); + } + + if (res) { + fmt.doFormatString(clauses.get(0), rw, formatParams, false); + } else { + formatParams.right(); + } + } else { + int res; + if (arrParams.length() >= 1) { + res = arrParams.getInt(0, "conditional choice", '['); + } else { + if (item == null) { + throw new IllegalArgumentException("No parameter provided for [ directive."); + } else if (!(item instanceof Number)) { + throw new IllegalFormatConversionException('[', item.getClass()); + } + res = ((Number) item).intValue(); + + formatParams.right(); + } + + if (mods.dollarMod) res -= 1; + + // System.err.printf("Attempting selection of clause %d of %d (%s) (default %s)\n", + // res, clauses.size(), clauses, defClause); + if (clauses.size() == 0 || res < 0 || res >= clauses.size()) { + // System.err.printf("Selecting default clause (res %d, max %d): %s\n", res, clauses.size(), defClause); + // int clauseNo = 0; + // for (String clause : clauses) { + // System.err.printf("... clause %d: %s\n", ++clauseNo, clause); + // } + + if (defClause != null) fmt.doFormatString(defClause, rw, formatParams, false); + } else { + String frmt = clauses.get(res); + + // System.out.printf("Selecting clause %d of %d (params %s): %s\n", res, clauses.size(), formatParams, frmt); + fmt.doFormatString(frmt, rw, formatParams, false); + } + } + } catch (EscapeException eex) { + // @NOTE 9/5/18 + // + // I am not sure if it is valid to error here. I'm not + // even sure that we need to handle this here, but I + // dunno + //if (eex.endIteration) + // throw new UnsupportedOperationException("Colon mod not allowed on escape marker without colon mod on iteration"); + throw eex; + } + + return; + } + +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/directives/Directive.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/Directive.java new file mode 100644 index 0000000..61abfc1 --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/Directive.java @@ -0,0 +1,72 @@ +package bjc.utils.ioutils.format.directives; + +import java.io.IOException; +import java.util.regex.Matcher; + +import bjc.utils.esodata.Tape; +import bjc.utils.ioutils.format.CLFormatter; +import bjc.utils.ioutils.format.CLModifiers; +import bjc.utils.ioutils.format.CLParameters; +import bjc.utils.ioutils.ReportWriter; + +/** + * A CL format directive. + * + * @author EVE + * + */ +@FunctionalInterface +public interface Directive { + /** + * Execute this format directive. + * + * @param sb + * The buffer the string is being output to. + * @param item + * The current parameter being passed + * @param mods + * The directive modifiers + * @param arrParams + * The prefix parameters to the directive + * @param tParams + * All of the provided format parameters + * @param dirMatcher + * The matcher for format directives + * @param fmt + * The formatter itself. + */ + public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters arrParams, Tape tParams, + Matcher dirMatcher, CLFormatter fmt) throws IOException; + + public static boolean isOpening(String str) { + return isOpening(str.charAt(0)); + } + + public static boolean isOpening(char dir) { + switch(dir) { + case '(': + case '<': + case '[': + case '{': + return true; + default: + return false; + } + } + + public static boolean isClosing(String str) { + return isClosing(str.charAt(0)); + } + + public static boolean isClosing(char dir) { + switch(dir) { + case ')': + case '>': + case ']': + case '}': + return true; + default: + return false; + } + } +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/directives/EscapeDirective.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/EscapeDirective.java new file mode 100644 index 0000000..74488ed --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/EscapeDirective.java @@ -0,0 +1,59 @@ +package bjc.utils.ioutils.format.directives; + +import bjc.utils.esodata.Tape; +import bjc.utils.ioutils.format.CLFormatter; +import bjc.utils.ioutils.format.CLModifiers; +import bjc.utils.ioutils.format.CLParameters; +import bjc.utils.ioutils.format.EscapeException; +import bjc.utils.ioutils.ReportWriter; + +import java.util.regex.Matcher; + +/** + * Implementation for the ^ directive. + * @author student + * + */ +public class EscapeDirective implements Directive { + + @Override + public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, + Tape formatParams, Matcher dirMatcher, CLFormatter fmt) { + boolean shouldExit; + + if (mods.dollarMod) formatParams.right(); + + switch(params.length()) { + case 0: + shouldExit = formatParams.atEnd(); + 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; + } + + if (mods.dollarMod) formatParams.left(); + + /* At negates it. */ + if(mods.atMod) shouldExit = !shouldExit; + + if(shouldExit) throw new EscapeException(mods.colonMod); + } + +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/directives/FreshlineDirective.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/FreshlineDirective.java new file mode 100644 index 0000000..3c02bfc --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/FreshlineDirective.java @@ -0,0 +1,34 @@ +package bjc.utils.ioutils.format.directives; + +import bjc.utils.esodata.Tape; +import bjc.utils.ioutils.format.CLFormatter; +import bjc.utils.ioutils.format.CLModifiers; +import bjc.utils.ioutils.format.CLParameters; +import bjc.utils.ioutils.ReportWriter; + +import java.io.IOException; +import java.util.regex.Matcher; + +/** + * Implement the & directive. + * @author student + * + */ +public class FreshlineDirective implements Directive { + + @Override + public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape tParams, + Matcher dirMatcher, CLFormatter fmt) throws IOException { + int nTimes = 1; + + if(params.length() >= 1) { + nTimes = params.getInt(0, "occurance count", '&'); + } + + if(rw.isLastCharNL()) nTimes -= 1; + + for(int i = 0; i < nTimes; i++) { + rw.write("\n"); + } + } +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/directives/GeneralNumberDirective.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/GeneralNumberDirective.java new file mode 100644 index 0000000..3eb741e --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/GeneralNumberDirective.java @@ -0,0 +1,55 @@ +package bjc.utils.ioutils.format.directives; + +import bjc.utils.ioutils.format.CLModifiers; +import bjc.utils.ioutils.format.CLParameters; +import bjc.utils.ioutils.ReportWriter; +import bjc.utils.math.NumberUtils; + +import java.io.IOException; + +/** + * Implementation skeleton for number directives. + * + * @author student + * + */ +public abstract class GeneralNumberDirective implements Directive { + protected static void handleNumberDirective(ReportWriter rw, CLModifiers mods, CLParameters params, int argidx, + long val, int radix) throws IOException { + /* + * 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 + 4)) { + commaChar = params.getCharDefault((argidx + 3), "comma character", 'R', ','); + } + if (params.length() >= (argidx + 5)) { + 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); + } + + rw.write(res); + } +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/directives/GotoDirective.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/GotoDirective.java new file mode 100644 index 0000000..7e30eab --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/GotoDirective.java @@ -0,0 +1,47 @@ +package bjc.utils.ioutils.format.directives; + +import bjc.utils.esodata.Tape; +import bjc.utils.ioutils.format.CLFormatter; +import bjc.utils.ioutils.format.CLModifiers; +import bjc.utils.ioutils.format.CLParameters; +import bjc.utils.ioutils.ReportWriter; + +import java.util.regex.Matcher; + +/** + * Implement the * directive. + * + * @author student + * + */ +public class GotoDirective implements Directive { + + @Override + public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape 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/clformat/src/main/java/bjc/utils/ioutils/format/directives/IterationDirective.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/IterationDirective.java new file mode 100644 index 0000000..2ce6309 --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/IterationDirective.java @@ -0,0 +1,171 @@ +package bjc.utils.ioutils.format.directives; + +import bjc.utils.esodata.SingleTape; +import bjc.utils.esodata.Tape; +import bjc.utils.ioutils.format.*; +import bjc.utils.ioutils.ReportWriter; + +import java.io.IOException; + +import java.util.Iterator; + +import java.util.IllegalFormatConversionException; +import java.util.regex.Matcher; + +/** + * Implements the { directive. + * + * @author student + * + */ +public class IterationDirective implements Directive { + + @Override + public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters arrParams, Tape tParams, + Matcher dirMatcher, CLFormatter fmt) throws IOException { + 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("}")) { + break; + } else { + /* Not a special directive. */ + condBody.append(dirMatcher.group()); + } + } + } + + String frmt = condBody.toString(); + Object iter = item; + + // System.err.printf("Iteration format \"%s\" (iter %s)\n", frmt, 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", '{'); + } + + int numItr = 0; + + if (mods.atMod && mods.colonMod) { + try { + do { + if (numItr > maxItr) break; + numItr += 1; + + if (!(iter instanceof Iterable)) { + throw new IllegalFormatConversionException('{', iter.getClass()); + } + + @SuppressWarnings("unchecked") + Iterable nitr = (Iterable) iter; + Tape nParams = new SingleTape<>(nitr); + + try { + fmt.doFormatString(frmt, rw, nParams, false); + } catch (EscapeException eex) { + if (eex.endIteration) { + if (tParams.atEnd()) { + throw eex; + } + } + } + + tParams.right(); + iter = tParams.item(); + } while (tParams.position() < tParams.size()); + } catch (EscapeException eex) { + } + } else if (mods.atMod) { + try { + while (!tParams.atEnd()) { + // System.err.printf("Iterating with format \"%s\"\n", frmt); + if (numItr > maxItr) break; + numItr += 1; + + fmt.doFormatString(frmt, rw, tParams, false); + } + } catch (EscapeException eex) { + if (eex.endIteration) + throw new UnsupportedOperationException("Colon mod not allowed on escape marker without colon mod on iteration"); + } + } else if (mods.colonMod) { + if (!(item instanceof Iterable)) { + throw new IllegalFormatConversionException('{', item.getClass()); + } + + try { + @SuppressWarnings("unchecked") + Iterable itb = (Iterable) item; + Iterator itr = itb.iterator(); + while (itr.hasNext()) { + Object obj = itr.next(); + + if (numItr > maxItr) break; + numItr += 1; + + if (!(obj instanceof Iterable)) { + throw new IllegalFormatConversionException('{', obj.getClass()); + } + + @SuppressWarnings("unchecked") + Iterable nitr = (Iterable) obj; + Tape nParams = new SingleTape<>(nitr); + + try { + fmt.doFormatString(frmt, rw, nParams, false); + } catch (EscapeException eex) { + if(eex.endIteration && !itr.hasNext()) throw eex; + } + } + } catch (EscapeException eex) { + } + } else { + if (!(item instanceof Iterable)) { + throw new IllegalFormatConversionException('{', item.getClass()); + } + + try { + @SuppressWarnings("unchecked") + Iterable itr = (Iterable) item; + Tape nParams = new SingleTape<>(itr); + + while (!nParams.atEnd()) { + if (numItr > maxItr) break; + numItr += 1; + + fmt.doFormatString(frmt, rw, nParams, false); + } + } catch (EscapeException eex) { + if (eex.endIteration) + throw new UnsupportedOperationException("Colon mod not allowed on escape marker without colon mod on iteration"); + } + } + + tParams.right(); + } +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/directives/LiteralDirective.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/LiteralDirective.java new file mode 100644 index 0000000..d833654 --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/LiteralDirective.java @@ -0,0 +1,51 @@ +package bjc.utils.ioutils.format.directives; + +import bjc.utils.esodata.Tape; +import bjc.utils.ioutils.format.CLFormatter; +import bjc.utils.ioutils.format.CLModifiers; +import bjc.utils.ioutils.format.CLParameters; +import bjc.utils.ioutils.ReportWriter; + +import java.io.IOException; +import java.util.regex.Matcher; + +/** + * Implements directives that create a literal string. + * + * @author student + * + */ +public class LiteralDirective implements Directive { + + private char directive; + private String lit; + + /** + * Create a new literal directive. + * + * @param lit + * The string for the directive. + * @param directive + * The character for the directive. + */ + public LiteralDirective(String lit, char directive) { + this.directive = directive; + this.lit = lit; + } + + @Override + public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape tParams, + Matcher dirMatcher, CLFormatter fmt) throws IOException { + int nTimes = 1; + + if (params.length() >= 1) { + nTimes = params.getInt(0, "occurance count", directive); + } + + for (int i = 0; i < nTimes; i++) { + rw.write(lit); + } + + } + +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/directives/NumberDirective.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/NumberDirective.java new file mode 100644 index 0000000..88b3e7e --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/NumberDirective.java @@ -0,0 +1,57 @@ +package bjc.utils.ioutils.format.directives; + +import java.io.IOException; +import java.util.IllegalFormatConversionException; +import java.util.regex.Matcher; + +import bjc.utils.esodata.Tape; +import bjc.utils.ioutils.format.CLFormatter; +import bjc.utils.ioutils.format.CLModifiers; +import bjc.utils.ioutils.format.CLParameters; +import bjc.utils.ioutils.ReportWriter; + +/** + * Implements radix based numbers. + * + * @author student + * + */ +public class NumberDirective extends GeneralNumberDirective { + + /** + * Create a new radix based number directive. + * + * @param argidx + * The argument offset to use. + * @param radix + * The radix of the number to use. + */ + public NumberDirective(int argidx, int radix, char directive) { + this.argidx = argidx; + this.radix = radix; + + this.directive = directive; + } + + private int argidx; + private int radix; + + private char directive; + + @Override + public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters params, Tape tParams, + Matcher dirMatcher, CLFormatter fmt) throws IOException { + CLFormatter.checkItem(item, directive); + + if (!(item instanceof Number)) { + throw new IllegalFormatConversionException(directive, item.getClass()); + } + + long val = ((Number) item).longValue(); + + handleNumberDirective(rw, mods, params, argidx, val, radix); + + tParams.right(); + } + +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/directives/RadixDirective.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/RadixDirective.java new file mode 100644 index 0000000..e8dd4b0 --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/RadixDirective.java @@ -0,0 +1,55 @@ +package bjc.utils.ioutils.format.directives; + +import bjc.utils.esodata.Tape; +import bjc.utils.ioutils.format.CLFormatter; +import bjc.utils.ioutils.format.CLModifiers; +import bjc.utils.ioutils.format.CLParameters; +import bjc.utils.ioutils.ReportWriter; +import bjc.utils.math.NumberUtils; + +import java.io.IOException; +import java.util.IllegalFormatConversionException; +import java.util.regex.Matcher; + +/** + * Generalized radix directive. + * + * @author student + * + */ +public class RadixDirective extends GeneralNumberDirective { + + @Override + public void format(ReportWriter rw, Object arg, CLModifiers mods, CLParameters params, Tape tParams, + Matcher dirMatcher, CLFormatter fmt) throws IOException { + 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) { + rw.write(NumberUtils.toRoman(val, mods.colonMod)); + } else if (mods.colonMod) { + rw.write(NumberUtils.toOrdinal(val)); + } else { + rw.write(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(rw, mods, params, 0, val, radix); + } + + tParams.right(); + } +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/directives/RecursiveDirective.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/RecursiveDirective.java new file mode 100644 index 0000000..44a25ad --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/RecursiveDirective.java @@ -0,0 +1,49 @@ +package bjc.utils.ioutils.format.directives; + +import bjc.utils.esodata.SingleTape; +import bjc.utils.esodata.Tape; +import bjc.utils.ioutils.format.*; +import bjc.utils.ioutils.ReportWriter; +import java.util.IllegalFormatConversionException; + +import java.io.IOException; +import java.util.regex.Matcher; + +public class RecursiveDirective implements Directive { + public void format(ReportWriter rw, Object arg, CLModifiers mods, CLParameters params, Tape tParams, + Matcher dirMatcher, CLFormatter fmt) throws IOException { + tParams.right(); + + CLFormatter.checkItem(arg, '?'); + + if (mods.atMod) { + if (!(arg instanceof String)) + throw new IllegalFormatConversionException('?', arg.getClass()); + + try { + fmt.doFormatString((String)arg, rw, tParams, true); + } catch (EscapeException eex) { + if (eex.endIteration) + throw new UnsupportedOperationException("Colon mod not allowed on escape marker without colon mod on iteration"); + } + } else { + if (tParams.atEnd()) + throw new IllegalArgumentException("? directive requires two format parameters"); + + Object o = tParams.item(); + tParams.right(); + + if (!(o instanceof Iterable)) + throw new IllegalFormatConversionException('?', o.getClass()); + + Iterable itb = (Iterable)o; + Tape newParams = new SingleTape<>(itb); + + try { + fmt.doFormatString((String)arg, rw, newParams, true); + } catch (EscapeException eex) { + throw new UnsupportedOperationException("Colon mod not allowed on escape marker without colon mod on iteration"); + } + } + } +} diff --git a/clformat/src/main/java/bjc/utils/ioutils/format/directives/TabulateDirective.java b/clformat/src/main/java/bjc/utils/ioutils/format/directives/TabulateDirective.java new file mode 100644 index 0000000..d9136f2 --- /dev/null +++ b/clformat/src/main/java/bjc/utils/ioutils/format/directives/TabulateDirective.java @@ -0,0 +1,80 @@ +package bjc.utils.ioutils.format.directives; + +import java.io.IOException; +import java.util.regex.Matcher; + +import bjc.utils.esodata.Tape; + +import bjc.utils.ioutils.ReportWriter; +import bjc.utils.ioutils.format.*; + +public class TabulateDirective implements Directive { + public void format(ReportWriter rw, Object item, CLModifiers mods, CLParameters arrParams, Tape tParams, + Matcher dirMatcher, CLFormatter fmt) throws IOException { + // Unsupported feature. + // + // I can't really make out what this is supposed to do from the + // documentation, but I suspect that it depends on font glyph + // size, not character positions + if (mods.colonMod) { + throw new UnsupportedOperationException("Colon mod is not supported for T directive"); + } + + // Support for a possible future feature + char padchar = ' '; + + if (mods.atMod) { + int colrel = 1, colinc = 1; + + if (arrParams.length() > 2) { + colinc = arrParams.getIntDefault(1, "column increment", 'T', 1); + } + + if (arrParams.length() > 1) { + colrel = arrParams.getIntDefault(0, "relative column number", 'T', 1); + } + + for (int i = 0; i < colrel; i++) { + rw.write(padchar); + } + + int currCol = rw.getLinePos(); + + int nSpaces = 0; + + while ((currCol + nSpaces) % colinc != 0) nSpaces++; + + for (int i = 0; i < nSpaces; i++) { + rw.write(padchar); + } + } else { + int colnum = 1, colinc = 1; + + if (arrParams.length() > 2) { + colinc = arrParams.getIntDefault(1, "column increment", 'T', 1); + } + + if (arrParams.length() > 1) { + colnum = arrParams.getIntDefault(0, "column number", 'T', 1); + } + + int currCol = rw.getLinePos(); + + if (currCol < colnum) { + for (int i = currCol; i < colnum; i++) { + rw.write(padchar); + } + } else { + if (colinc == 0) return; + + int k = 0; + + while (colnum > (currCol + (k * colinc))) k++; + + for (int i = currCol; i < colnum; i++) { + rw.write(padchar); + } + } + } + } +} diff --git a/clformat/src/test/java/bjc/AppTest.java b/clformat/src/test/java/bjc/AppTest.java new file mode 100644 index 0000000..6f70e21 --- /dev/null +++ b/clformat/src/test/java/bjc/AppTest.java @@ -0,0 +1,20 @@ +package bjc; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/clformat/src/test/java/bjc/utils/test/ioutils/CLFormatterTest.java b/clformat/src/test/java/bjc/utils/test/ioutils/CLFormatterTest.java new file mode 100644 index 0000000..08dfe78 --- /dev/null +++ b/clformat/src/test/java/bjc/utils/test/ioutils/CLFormatterTest.java @@ -0,0 +1,128 @@ +package bjc.utils.test.ioutils; + +import static org.junit.Assert.*; + +import java.io.IOException; + +import java.util.Arrays; + +import bjc.utils.ioutils.format.CLFormatter; + +import org.junit.Test; + +import static java.util.Arrays.asList; +/** + * Tests for CL format strings. + * + * @author EVE + * + */ +@SuppressWarnings("javadoc") +public class CLFormatterTest { + private CLFormatter fmt = new CLFormatter(); + + @Test + public void testLiteralString() { + // Print literal strings exactly + assertEquals("foo", format("foo")); + } + + @Test + public void testDecimalPrinting() { + // Test decimal printing + assertEquals("5", format("~D", 5)); + assertEquals(" 5", format("~3D", 5)); + assertEquals("005", format("~3,'0D", 5)); + assertEquals("6|55|35", format("~,,'|,2:D", 0xFFFF)); + } + + @Test + public void testRadixPrinting() { + // Test radix printing + assertEquals("1 22", format("~3,,,' ,2:R", 17)); + } + + @Test + public void testBinaryPrinting() { + // Test binary printing + assertEquals("1101", format("~,,' ,4:B", 13)); + assertEquals("1 0001", format("~,,' ,4:B", 17)); + // @NOTE 9/6/18 :CommaPad + // + // I'm not sure how this is the expected behavior, unless the + // comma interval is enforced in the case of a digit padchar. + // assertEquals("0000 1101 0000 0101", format("~19,0,' ,4:B", 3333)); + assertEquals("000001101 0000 0101", format("~19,0,' ,4:B", 3333)); + } + + @Test + public void testConditionalPrinting() { + // Test conditional printing + assertEquals("print length = 5", format("~@[print level = ~D~]~@[print length = ~D~]", null, 5)); + } + + @Test + public void testIterationPrinting() { + assertEquals("The winners are: fred harry jill.", format("The winners are:~{ ~S~}.", + asList("fred", "harry", "jill"))); + assertEquals("Pairs: (1, 1) (2, 2) (3, 3).", format("Pairs:~{ (~S, ~S)~}.", + asList(1, 1, 2, 2, 3, 3))); + + assertEquals("Pairs: (1, 1) (2, 2) (3, 3).", format("Pairs:~:{ (~S, ~S)~}.", + asList(asList(1, 1), asList(2, 2), asList(3, 3)))); + + assertEquals("Pairs: (1, 1) (2, 2) (3, 3).", format("Pairs:~@{ (~S, ~S)~}.", + 1, 1, 2, 2, 3, 3)); + + assertEquals("Pairs: (1, 1) (2, 2) (3, 3).", format("Pairs:~:@{ (~S, ~S)~}.", + asList(1, 1), asList(2, 2), asList(3, 3))); + } + + @Test + public void testRecursivePrinting() { + assertEquals(" 7", format("~? ~D", "<~A ~D>", asList("Foo", 5), 7)); + assertEquals(" 7", format("~? ~D", "<~A ~D>", asList("Foo", 5, 14), 7)); + + assertEquals(" 7", format("~@? ~D", "<~A ~D>", "Foo", 5, 7)); + assertEquals(" 14", format("~@? ~D", "<~A ~D>", "Foo", 5, 14, 7)); + } + + @Test + public void testEscapePrinting() { + assertEquals("Done.", format("Done.~^ ~D warning.~^ ~D error.")); + assertEquals("Done. 3 warning.", format("Done.~^ ~D warning.~^ ~D error.", 3)); + assertEquals("Done. 1 warning. 5 error.", format("Done.~^ ~D warning.~^ ~D error.", 1, 5)); + } + + @Test + public void testCapsPrinting() { + assertEquals("XIV xiv", format("~@R ~(~@R~)", 14, 14)); + } + + @Test + public void testListPrinting() { + // Test printing a list + // String fmtStr = "Items:~#[ none~; ~A~; ~A and ~A~:;~@{~#[~; and~] ~A~^,~}~]."; + String fmtStr = "Items:~#[ none~; ~A~; ~A and ~A~:;~@{~#*[ ~A,~; and ~A~; ~A~]~}~]."; + + assertEquals("Items: none.", format(fmtStr)); + assertEquals("Items: foo.", format(fmtStr, "foo")); + assertEquals("Items: foo and bar.", format(fmtStr, "foo", "bar")); + assertEquals("Items: foo, bar and baz.", format(fmtStr, "foo", "bar", "baz")); + assertEquals("Items: foo, bar, baz and quux.", format(fmtStr, "foo", "bar", "baz", "quux")); + } + + @Test + public void testRandomCases() { + // Random test cases + assertEquals("3 dogs are here", format("~D dog~:[s are~; is~] here", 3, 3 == 1)); + } + + private String format(String str, Object... params) { + try { + return fmt.formatString(str, params); + } catch (IOException ioex) { + return null; + } + } +} diff --git a/pom.xml b/pom.xml index 18364f0..c521837 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,5 @@ - + + 4.0.0 bjc @@ -20,5 +20,6 @@ base - - + clformat + + \ No newline at end of file -- cgit v1.2.3