diff options
Diffstat (limited to 'src/main/java/bjc/everge/Everge.java')
| -rw-r--r-- | src/main/java/bjc/everge/Everge.java | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/src/main/java/bjc/everge/Everge.java b/src/main/java/bjc/everge/Everge.java index 257b242..9b5a235 100644 --- a/src/main/java/bjc/everge/Everge.java +++ b/src/main/java/bjc/everge/Everge.java @@ -1,5 +1,30 @@ package bjc.everge; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; + +import java.nio.charset.Charset; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.Scanner; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + /** * Everge front-end application. * @@ -7,12 +32,404 @@ package bjc.everge; */ public class Everge { /** + * Details how we handle our input. + */ + public static enum InputStatus { + /** + * Process the input as a single string. + */ + ALL, + /** + * Process the input line-by-line. + */ + LINE, + /** + * Process the input, splitting it around occurances of a regex. + */ + REGEX; + } + + // Options for doing repl-pairs + private ReplOpts ropts = new ReplOpts(); + + // Loaded repl-pairs + private List<ReplPair> lrp = new ArrayList<>(); + + // Input status + private InputStatus inputStat = InputStatus.ALL; + + // Are we processing CLI args? (haven't seen a -- yet) + private boolean doingArgs = true; + + // Should an NL be printed after each replace? + private boolean printNL = true; + + // Verbosity level + private int verbosity = 0; + + // The pattern to use for REGEX input mode + private String pattern; + + // The queue of arguments to process + private Deque<String> argQue = new LinkedList<>(); + + // Used to prevent inter-mixing argument alterations with input processing. + private ReadWriteLock argLock = new ReentrantReadWriteLock(); + + // Input/output streams + public PrintStream outStream = System.out; + public PrintStream errStream = System.err; + + /** * Main method for front end, * * @param args * The CLI arguments. */ public static void main(String[] args) { + Everge evg = new Everge(); + + evg.processArgs(args); + } + + /** + * Process one or more command line arguments. + * + * @param args + * The arguments to process. + * @return Whether we processed succesfully or not. + */ + public boolean processArgs(String... args) { + List<String> errs = new ArrayList<>(); + + boolean stat = processArgs(errs, args); + if (!stat) { + for (String err : errs) { + errStream.println(err); + } + } + + return stat; + } + + /** + * Process one or more command line arguments. + * + * @param args + * The arguments to process. + * @param errs + * The list to stash errors in. + * @return Whether we processed succesfully or not. + */ + public boolean processArgs(List<String> errs, String... args) { + argLock.writeLock().lock(); + + boolean retStat = true; + + try { + loadQueue(args); + + // Process CLI args + while(argQue.size() > 0) { + String arg = argQue.pop(); + + if (arg.equals("--")) { + doingArgs = false; + continue; + } + + // Process an argument + if (doingArgs && arg.startsWith("-")) { + String argName = arg; + String argBody = ""; + + // Process arguments to arguments + int idx = arg.indexOf("="); + if (idx != -1) { + argName = arg.substring(0, idx); + argBody = arg.substring(idx + 1); + } + + switch (argName) { + case "-n": + case "--newline": + printNL = true; + break; + case "-N": + case "--no-newline": + printNL = false; + break; + case "-v": + case "--verbose": + verbosity += 1; + break; + case "-q": + case "--quiet": + verbosity -= 1; + break; + case "--verbosity": + if (argQue.size() < 1) { + errs.add("[ERROR] No parameter to --verbosity"); + retStat = false; + break; + } + argBody = argQue.pop(); + break; + case "-V": + try { + verbosity = Integer.parseInt(argBody); + } catch (NumberFormatException nfex) { + String msg = String.format("[ERROR] Invalid verbosity: '%s' is not an integer", + argBody); + errs.add(msg); + retStat = false; + } + break; + case "--pattern": + if (argQue.size() < 1) { + errs.add("[ERROR] No parameter to --pattern"); + retStat = false; + break; + } + argBody = argQue.pop(); + case "-p": + try { + pattern = argBody; + + Pattern.compile(argBody); + } catch (PatternSyntaxException psex) { + String msg = String.format("[ERROR] Pattern '%s' is invalid: %s", + pattern, psex.getMessage()); + errs.add(msg); + retStat = false; + } + break; + case "--file": + if (argQue.size() < 1) { + errs.add("[ERROR] No argument to --file"); + retStat = false; + break; + } + argBody = argQue.pop(); + case "-f": + try (FileInputStream fis = new FileInputStream(argBody); + Scanner scn = new Scanner(fis)) { + List<ReplError> ferrs = new ArrayList<>(); + + lrp = ReplPair.readList(lrp, scn, ferrs, ropts); + + if (ferrs.size() > 0) { + StringBuilder sb = new StringBuilder(); + + String errString = "an error"; + if (ferrs.size() > 1) errString = String.format("%d errors"); + + { + String msg = String.format( + "[ERROR] Encountered errors parsing data file'%s'\n", + argBody); + sb.append(msg); + } + + for (ReplError err : ferrs) { + sb.append(String.format("\t%s\n", err)); + } + + errs.add(sb.toString()); + retStat = false; + } + } catch (FileNotFoundException fnfex) { + String msg = String.format("[ERROR] Could not open data file '%s' for input", + argBody); + errs.add(msg); + retStat = false; + } catch (IOException ioex) { + String msg = String.format("[ERROR] Unknown I/O error reading data file '%s': %s", + argBody, ioex.getMessage()); + errs.add(msg); + retStat = false; + } + break; + case "--arg-file": + if (argQue.size() < 1) { + errs.add("[ERROR] No argument to --arg-file"); + break; + } + argBody = argQue.pop(); + case "-F": + try (FileInputStream fis = new FileInputStream(argBody); + Scanner scn = new Scanner(fis)) { + List<String> sl = new ArrayList<>(); + + while (scn.hasNextLine()) { + String ln = scn.nextLine().trim(); + + if (ln.equals("")) continue; + if (ln.startsWith("#")) continue; + + sl.add(ln); + } + + processArgs(sl.toArray(new String[0])); + } catch (FileNotFoundException fnfex) { + String msg = String.format("[ERROR] Could not open argument file '%s' for input", argBody); + errs.add(msg); + retStat = false; + } catch (IOException ioex) { + String msg = String.format("[ERROR] Unknown I/O error reading input file '%s': %s", + argBody, ioex.getMessage()); + errs.add(msg); + retStat = false; + } + break; + default: + { + String msg = String.format("[ERROR] Unrecognised CLI argument name '%s'\n", argName); + errs.add(msg); + retStat = false; + } + } + } else { + // Strip off an escaped initial dash + if (arg.startsWith("\\-")) arg = arg.substring(1); + + processInputFile(arg); + } + } + } finally { + argLock.writeLock().unlock(); + } + + return retStat; + } + + /** + * Process a input file. + * + * @param fle + * Input file to process. + * @return Whether we processed succesfully or not. + */ + public boolean processInputFile(String fle) { + List<String> errs = new ArrayList<>(); + + boolean stat = processInputFile(errs, fle); + if (!stat) { + for (String err : errs) { + errStream.println(err); + } + } + + return stat; + } + + /** + * Process a input file. + * + * @param fle + * Input file to process. + * @param errs + * List to accumulate errors in. + * @return Whether we processed succesfully or not. + */ + public boolean processInputFile(List<String> errs, String fle) { + argLock.readLock().lock(); + + // Read in and do replacements on a file + try { + if (verbosity > 2) { + errStream.printf("[TRACE] Reading file (%s) in mode (%s)\n", fle, inputStat); + } + + if (inputStat == InputStatus.ALL) { + Path pth = Paths.get(fle); + + if (!Files.isReadable(pth)) { + String msg = String.format("[ERROR] File '%s' is not readable\n", fle); + errs.add(msg); + return false; + } else { + byte[] inp = Files.readAllBytes(pth); + + String strang = new String(inp, Charset.forName("UTF-8")); + + processString(strang); + } + } else if (inputStat == InputStatus.LINE) { + try (FileInputStream fis = new FileInputStream(fle); Scanner scn = new Scanner(fis)) { + while(scn.hasNextLine()) { + processString(scn.nextLine()); + } + } + } else if (inputStat == InputStatus.REGEX) { + try (FileInputStream fis = new FileInputStream(fle); Scanner scn = new Scanner(fis)) { + scn.useDelimiter(pattern); + + while(scn.hasNext()) { + processString(scn.next()); + } + } + } else { + String msg = String.format("[INTERNAL-ERROR] Input status '%s' is not yet implemented\n", + inputStat); + errs.add(msg); + return false; + } + } catch (IOException ioex) { + String msg = String.format("[ERROR] Unknown I/O related error for file '%s'\n\tError was %s", + fle, ioex.getMessage()); + errs.add(msg); + return false; + } finally { + argLock.readLock().unlock(); + } + + return true; + } + + /** + * Process an input string. + * + * @param inp + * The input string to process. + */ + public void processString(String inp) { + argLock.readLock().lock(); + + try { + String strang = inp; + + for (ReplPair rp : lrp) { + strang = rp.apply(strang); + } + + outStream.print(strang); + if (printNL) outStream.println(); + } finally { + argLock.readLock().unlock(); + } + } + + // Load arguments into the argument queue. + private void loadQueue(String... args) { + boolean doArgs = true; + for (String arg : args) { + if (arg.equals("--")) doArgs = false; + // Handle things like -nNv correctly + if (doArgs) { + if (arg.startsWith("-") && !arg.startsWith("--")) { + char[] car = arg.substring(1).toCharArray(); + for (char c : car) { + String argstr = String.format("-%c", c); + argQue.add(argstr); + } + } else { + argQue.add(arg); + } + } else { + argQue.add(arg); + } + } } } |
