summaryrefslogtreecommitdiff
path: root/src/main/java/bjc/everge/Everge.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/bjc/everge/Everge.java')
-rw-r--r--src/main/java/bjc/everge/Everge.java417
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);
+ }
+ }
}
}