From 5bb438d5f6d21ca54395ce8e82bb1b9800bd1725 Mon Sep 17 00:00:00 2001 From: "Benjamin J. Culkin" Date: Mon, 18 Sep 2017 15:45:58 -0300 Subject: More work on formats. --- .../main/java/bjc/utils/ioutils/CLFormatter.java | 408 ++++++++++++++++----- 1 file changed, 317 insertions(+), 91 deletions(-) diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/CLFormatter.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/CLFormatter.java index ca9c32e..eefd532 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/CLFormatter.java +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/CLFormatter.java @@ -2,6 +2,8 @@ package bjc.utils.ioutils; import java.util.HashMap; import java.util.IllegalFormatConversionException; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.UnknownFormatConversionException; import java.util.regex.Matcher; @@ -16,6 +18,39 @@ import static bjc.utils.PropertyDB.getCompiledRegex; import static bjc.utils.PropertyDB.getRegex; public class CLFormatter { + public static class CLModifiers { + public final boolean atMod; + public final boolean colonMod; + + public CLModifiers(boolean at, boolean colon) { + atMod = at; + colonMod = colon; + } + + public static CLModifiers fromString(String modString) { + boolean atMod = false; + boolean colonMod = false; + if(modString != null) { + atMod = modString.contains("@"); + colonMod = modString.contains(":"); + } + + return new CLModifiers(atMod, colonMod); + } + } + + public static class EscapeException extends RuntimeException { + public final boolean endIteration; + + public EscapeException() { + endIteration = false; + } + + public EscapeException(boolean end) { + endIteration = end; + } + } + @FunctionalInterface public interface Directive { /* @@ -32,7 +67,7 @@ public class CLFormatter { private static final String prefixList = applyFormat("delimSeparatedList", prefixParam, ","); private static final String directiveName = getRegex("clFormatName"); - + private static final String formatDirective = applyFormat("clFormatDirective", prefixList, formatMod, directiveName); private static final Pattern pFormatDirective = Pattern.compile(formatDirective); @@ -49,13 +84,17 @@ public class CLFormatter { public String formatString(String format, Object... params) { StringBuffer sb = new StringBuffer(); - Matcher dirMatcher = pFormatDirective.matcher(format); - - /* - * Put the parameters where we can easily handle them. - */ + /* Put the parameters where we can easily handle them. */ Tape tParams = new SingleTape(params); + doFormatString(format, sb, tParams); + + return sb.toString(); + } + + private void doFormatString(String format, StringBuffer sb, Tape tParams) { + Matcher dirMatcher = pFormatDirective.matcher(format); + while(dirMatcher.find()) { dirMatcher.appendReplacement(sb, ""); @@ -65,13 +104,7 @@ public class CLFormatter { String dirParams = dirMatcher.group("params"); CLParameters arrParams = CLParameters.fromDirective(dirParams.split("(?": + /* @TODO + * Figure out how to implement + * tabulation/justification in a + * reasonable manner. + */ + throw new IllegalArgumentException("Layout-control directives aren't implemented yet."); + case "F": + case "E": + case "G": + case "$": + /* @TODO implement floating point directives. */ + throw new IllegalArgumentException("Floating-point directives aren't implemented yet."); + case "S": + case "W": + /* @TODO + * figure out if we want to implement + * someting for these directives instead + * of punting. + * */ + throw new IllegalArgumentException("S and W aren't implemented. Use A instead"); + default: + String msg = String.format("Unknown format directive '%s'", dirName); + throw new UnknownFormatConversionException(msg); } } dirMatcher.appendTail(sb); - - return sb.toString(); } - private void handleCDirective(StringBuffer buff, Object parm, boolean atMod, boolean colonMod) { + private void handleCDirective(StringBuffer buff, Object parm, CLModifiers mods) { if(!(parm instanceof Character)) { throw new IllegalFormatConversionException('C', parm.getClass()); } @@ -159,7 +227,7 @@ public class CLFormatter { char ch = (Character) parm; int codepoint = (int) ch; - if(colonMod) { + if(mods.colonMod) { /* * Colon mod means print Unicode character name. */ @@ -195,7 +263,7 @@ public class CLFormatter { } } - private void handleNumberDirective(StringBuffer buff, boolean atMod, boolean colonMod, CLParameters params, int argidx, long val, int radix) { + private void handleNumberDirective(StringBuffer buff, CLModifiers mods, CLParameters params, int argidx, long val, int radix) { /* * Initialize the two padding related parameters, and * then fill them in from the directive parameters if @@ -210,7 +278,7 @@ public class CLFormatter { padchar = params.getCharDefault(argidx + 2, "padding character", 'R', ' '); } - if(colonMod) { + if(mods.colonMod) { /* * We're doing commas, so check if the two * comma-related parameters were supplied. @@ -224,13 +292,13 @@ public class CLFormatter { commaInterval = params.getIntDefault((argidx + 4), "comma interval", 'R', 0); } - NumberUtils.toCommaString(val, mincol, padchar, commaInterval, commaChar, atMod, radix); + NumberUtils.toCommaString(val, mincol, padchar, commaInterval, commaChar, mods.atMod, radix); } else { - NumberUtils.toNormalString(val, mincol, padchar, atMod, radix); + NumberUtils.toNormalString(val, mincol, padchar, mods.atMod, radix); } } - private void handleRadixDirective(StringBuffer buff, boolean atMod, boolean colonMod, CLParameters params, Object arg) { + private void handleRadixDirective(StringBuffer buff, CLModifiers mods, CLParameters params, Object arg) { if(!(arg instanceof Number)) { throw new IllegalFormatConversionException('R', arg.getClass()); } @@ -241,9 +309,9 @@ public class CLFormatter { long val = ((Number)arg).longValue(); if(params.length() == 0) { - if(atMod) { - buff.append(NumberUtils.toRoman((Long)val, colonMod)); - } else if(colonMod) { + if(mods.atMod) { + buff.append(NumberUtils.toRoman((Long)val, mods.colonMod)); + } else if(mods.colonMod) { buff.append(NumberUtils.toOrdinal(val)); } else { buff.append(NumberUtils.toCardinal(val)); @@ -254,11 +322,11 @@ public class CLFormatter { int radix = params.getInt(0, "radix", 'R'); - handleNumberDirective(buff, atMod, colonMod, params, 0, val, radix); + handleNumberDirective(buff, mods, params, 0, val, radix); } } - private void handleAestheticDirective(StringBuffer buff, Object item, boolean atMod, boolean colonMod, CLParameters arrParams) { + private void handleAestheticDirective(StringBuffer buff, Object item, CLModifiers mods, CLParameters params) { int mincol = 0, colinc = 1, minpad = 0; char padchar = ' '; @@ -276,7 +344,7 @@ public class CLFormatter { StringBuilder work = new StringBuilder(); - if(atMod) { + if(mods.atMod) { for(int i = 0; i < minpad; i++) { work.append(padchar); } @@ -290,7 +358,7 @@ public class CLFormatter { work.append(item.toString()); - if(!atMod) { + if(!mods.atMod) { for(int i = 0; i < minpad; i++) { work.append(padchar); } @@ -302,4 +370,162 @@ public class CLFormatter { } } } + + private void handleGotoDirective(CLModifiers mods, CLParameters params, Tape formatParams) { + if(mods.colonMod) { + int num = 1; + if(params.length() > 1) { + num = params.getIntDefault(0, "number of arguments backward", '*', 1); + } + + formatParams.left(num); + } else if(mods.atMod) { + int num = 0; + if(params.length() > 1) { + num = params.getIntDefault(0, "argument index", '*', 0); + } + + formatParams.first(); + formatParams.right(num); + } else { + int num = 1; + if(params.length() > 1) { + num = params.getIntDefault(0, "number of arguments forward", '*', 1); + } + + formatParams.right(num); + } + } + + private void handleConditionalDirective(StringBuffer sb, CLModifiers mods, CLParameters arrParams, Tape formatParams, Matcher dirMatcher) { + StringBuffer condBody = new StringBuffer(); + + List clauses = new ArrayList<>(); + String defClause = null; + boolean isDefault = false; + + while(dirMatcher.find()) { + /* Process a list of clauses. */ + String dirName = dirMatcher.group("name"); + String dirMods = dirMatcher.group("modifiers"); + + if(dirName != null) { + /* Append everything up to this directive. */ + dirMatcher.appendReplacement(condBody, ""); + + if(dirName.equals("]")) { + /* End the conditional. */ + String clause = condBody.toString(); + if(isDefault) { + defClause = clause; + } else { + clauses.add(clause); + } + + break; + } else if(dirName.equals(";")) { + /* End the clause. */ + String clause = condBody.toString(); + if(isDefault) { + defClause = clause; + } else { + clauses.add(clause); + } + + /* Mark the next clause as the default. */ + if(dirMods.contains(":")) { + isDefault = true; + } + } else { + /* Not a special directive. */ + condBody.append(dirMatcher.group()); + } + } + } + + Object par = formatParams.item(); + if(mods.colonMod) { + formatParams.right(); + + if(par == null) { + throw new IllegalArgumentException("No parameter provided for [ directive."); + } else if(!(par instanceof Boolean)) { + throw new IllegalFormatConversionException('[', par.getClass()); + } + boolean res = (Boolean)par; + + String fmt; + if(res) fmt = clauses.get(1); + else fmt = clauses.get(0); + + doFormatString(fmt, sb, formatParams); + } else if(mods.atMod) { + if(par == null) { + throw new IllegalArgumentException("No parameter provided for [ directive."); + } else if(!(par instanceof Boolean)) { + throw new IllegalFormatConversionException('[', par.getClass()); + } + boolean res = (Boolean)par; + + if(res) { + doFormatString(clauses.get(0), sb, formatParams); + } else { + formatParams.right(); + } + } else { + int res; + if(arrParams.length() > 1) { + res = arrParams.getInt(0, "conditional choice", '['); + } else { + if(par == null) { + throw new IllegalArgumentException("No parameter provided for [ directive."); + } else if(!(par instanceof Number)) { + throw new IllegalFormatConversionException('[', par.getClass()); + } + res = ((Number)par).intValue(); + + formatParams.right(); + } + + if(res < 0 || res > clauses.size()) { + if(defClause != null) doFormatString(defClause, sb, formatParams); + } else { + doFormatString(clauses.get(res), sb, formatParams); + } + } + return; + } + + private void handleEscapeDirective(CLModifiers mods, CLParameters params, Tape formatParams) { + boolean shouldExit; + + switch(params.length()) { + case 0: + shouldExit = formatParams.size() == 0; + break; + case 1: + int num = params.getInt(0, "condition count", '^'); + shouldExit = num == 0; + break; + case 2: + int left = params.getInt(0, "left-hand condition", '^'); + int right = params.getInt(1, "right-hand condition", '^'); + shouldExit = left == right; + break; + case 3: + default: + int low = params.getInt(0, "lower-bound condition", '^'); + int mid = params.getInt(1, "interval condition", '^'); + int high = params.getInt(2, "upper-bound condition", '^'); + shouldExit = (low <= mid) && (mid <= high); + break; + } + + /* At negates it. */ + if(mods.atMod) shouldExit = !shouldExit; + + if(shouldExit) throw new EscapeException(mods.colonMod); + } + + } -- cgit v1.2.3