From e5427ff5c84d0171712db8dc9978e4d49916bcb7 Mon Sep 17 00:00:00 2001
From: Arkerthan <arkerthan@gmail.com>
Date: Mon, 11 Mar 2019 02:38:19 +0100
Subject: [PATCH] initial commit java sanityCheck and sources

---
 SanityCheck.jar                               | Bin 0 -> 14205 bytes
 devTools/javaSanityCheck/excluded             |  16 +
 devTools/javaSanityCheck/htmlTags             |  44 ++
 devTools/javaSanityCheck/src/Main.java        | 150 +++++
 .../javaSanityCheck/src/StringSearchTree.java |  47 ++
 devTools/javaSanityCheck/src/SyntaxError.java |  40 ++
 .../javaSanityCheck/src/UnknownState.java     |   8 +
 .../src/element/AngleBracketElement.java      | 515 ++++++++++++++++++
 .../src/element/AngleBracketElement2.java     | 147 +++++
 .../src/element/AtElement.java                |  72 +++
 .../javaSanityCheck/src/element/Element.java  |  17 +
 .../src/element/KnownElement.java             |  44 ++
 sanityCheck-java                              |   1 +
 13 files changed, 1101 insertions(+)
 create mode 100644 SanityCheck.jar
 create mode 100644 devTools/javaSanityCheck/excluded
 create mode 100644 devTools/javaSanityCheck/htmlTags
 create mode 100644 devTools/javaSanityCheck/src/Main.java
 create mode 100644 devTools/javaSanityCheck/src/StringSearchTree.java
 create mode 100644 devTools/javaSanityCheck/src/SyntaxError.java
 create mode 100644 devTools/javaSanityCheck/src/UnknownState.java
 create mode 100644 devTools/javaSanityCheck/src/element/AngleBracketElement.java
 create mode 100644 devTools/javaSanityCheck/src/element/AngleBracketElement2.java
 create mode 100644 devTools/javaSanityCheck/src/element/AtElement.java
 create mode 100644 devTools/javaSanityCheck/src/element/Element.java
 create mode 100644 devTools/javaSanityCheck/src/element/KnownElement.java
 create mode 100755 sanityCheck-java

diff --git a/SanityCheck.jar b/SanityCheck.jar
new file mode 100644
index 0000000000000000000000000000000000000000..42a086d8c043eb99010faa0dfcca5dc778ad0e1d
GIT binary patch
literal 14205
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
F{{UdwDvSUC

literal 0
HcmV?d00001

diff --git a/devTools/javaSanityCheck/excluded b/devTools/javaSanityCheck/excluded
new file mode 100644
index 00000000000..1b1e091323a
--- /dev/null
+++ b/devTools/javaSanityCheck/excluded
@@ -0,0 +1,16 @@
+#Add files or folders to be excluded.
+#empty lines will match on ALL paths, so if you need one do it like this:
+#
+src/art/
+src/gui/svgFilters.tw
+#
+#excluded to reduce false positives until better solution:
+src/pregmod/basenationalitiesControls.tw
+src/pregmod/editGenetics.tw
+src/pregmod/widgets/bodySwapReaction.tw
+src/uncategorized/costsBudget.tw
+src/uncategorized/initRules.tw
+src/uncategorized/slaveAssignmentsReport.tw
+src/uncategorized/storyCaption.tw
+#
+#reincluded later:
diff --git a/devTools/javaSanityCheck/htmlTags b/devTools/javaSanityCheck/htmlTags
new file mode 100644
index 00000000000..c306acc52dd
--- /dev/null
+++ b/devTools/javaSanityCheck/htmlTags
@@ -0,0 +1,44 @@
+#Allowed HTML tags
+#Effectively everything that is allowed in one of these statements like this:
+#<tag>, <tag ???> or </tag>
+#Do not add Twine Tags here.
+#Characters outside of ASCII scope are not supported.
+#
+#included to reduce false positives until better solution
+!--
+http://www.gnu.org/licenses/
+#html tags
+a
+b
+blockquote
+body
+br
+button
+caption
+center
+dd
+div
+dl
+dt
+h1
+h2
+h3
+h4
+hr
+html
+i
+img
+input
+li
+option
+script
+select
+span
+strong
+style
+table
+td
+th
+tr
+tt
+ul
diff --git a/devTools/javaSanityCheck/src/Main.java b/devTools/javaSanityCheck/src/Main.java
new file mode 100644
index 00000000000..e1f7909624c
--- /dev/null
+++ b/devTools/javaSanityCheck/src/Main.java
@@ -0,0 +1,150 @@
+package org.arkerthan.sanityCheck;
+
+import org.arkerthan.sanityCheck.element.AngleBracketElement;
+import org.arkerthan.sanityCheck.element.AngleBracketElement2;
+import org.arkerthan.sanityCheck.element.AtElement;
+import org.arkerthan.sanityCheck.element.Element;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Stack;
+
+public class Main {
+
+    public static StringSearchTree htmlTags;
+    private static String currentFile;
+    private static int currentLine, currentPosition;
+    private static Stack<Element> stack;
+    private static List<SyntaxError> errors = new LinkedList<>();
+    private static String[] excluded;
+
+    public static void main(String[] args) {
+        setupExclude();
+        setupHtmlTags();
+
+        // replace this with a known encoding if possible
+        Charset encoding = Charset.defaultCharset();
+        for (String filename : args) {
+            if (!excluded(filename)) {
+                currentFile = filename;
+                currentLine = 1;
+                stack = new Stack<>();
+                File file = new File(filename);
+                try {
+                    handleFile(file, encoding);
+                } catch (IOException e) {
+                    System.err.println("Couldn't read " + filename);
+                }
+            }
+        }
+
+        //handle errors
+        for (SyntaxError e :
+                errors) {
+            System.out.println(e.getError());
+        }
+    }
+
+    private static void setupHtmlTags() {
+        //preparing excluding folders
+        List<String> htmlTagsList = new ArrayList<>();
+        try {
+
+            Files.lines(new File("devTools/javaSanityCheck/htmlTags").toPath()).map(String::trim)
+                    .filter(s -> !s.startsWith("#"))
+                    .forEach(htmlTagsList::add);
+        } catch (IOException e) {
+            System.err.println("Couldn't read devTools/javaSanityCheck/htmlTags");
+        }
+
+        try {
+            htmlTags = StringSearchTree.generateTree(htmlTagsList);
+        } catch (ArrayIndexOutOfBoundsException e) {
+            System.err.println("Illegal Character in devTools/javaSanityCheck/htmlTags");
+            System.exit(-1);
+        }
+    }
+
+    private static void setupExclude() {
+        //preparing excluding folders
+        List<String> excludedList = new ArrayList<>();
+        try {
+
+            Files.lines(new File("devTools/javaSanityCheck/excluded").toPath()).map(String::trim)
+                    .filter(s -> !s.startsWith("#"))
+                    .forEach(excludedList::add);
+        } catch (IOException e) {
+            System.err.println("Couldn't read devTools/javaSanityCheck/excluded");
+        }
+
+        excluded = excludedList.toArray(new String[0]);
+    }
+
+    private static boolean excluded(String s) {
+        for (String ex :
+                excluded) {
+            if (s.startsWith(ex)) return true;
+        }
+        return false;
+    }
+
+
+    private static void handleFile(File file, Charset encoding) throws IOException {
+        try (InputStream in = new FileInputStream(file);
+             Reader reader = new InputStreamReader(in, encoding);
+             // buffer for efficiency
+             Reader buffer = new BufferedReader(reader)) {
+            handleCharacters(buffer);
+        }
+    }
+
+    private static void handleCharacters(Reader reader) throws IOException {
+        int r;
+        while ((r = reader.read()) != -1) {
+            char c = (char) r;
+            handleCharacter(c);
+        }
+    }
+
+    private static void handleCharacter(char c) {
+        currentPosition++;
+        if (c == '\n') {
+            currentLine++;
+            currentPosition = 1;
+        }
+
+        //try applying to the innermost element
+        if (!stack.empty()) {
+            int change;
+            try {
+                change = stack.peek().handleChar(c);
+            } catch (SyntaxError e) {
+                e.setFile(currentFile);
+                e.setLine(currentLine);
+                e.setPosition(currentPosition);
+                change = e.getChange();
+                errors.add(e);
+            }
+            if (change > 0) {
+                if (change == 2) {
+                    stack.pop();
+                }
+                return;
+            }
+        }
+
+        //innermost element was uninterested, trying to find matching element
+        switch (c) {
+            case '@':
+                stack.push(new AtElement());
+                break;
+            case '<':
+                stack.push(new AngleBracketElement2());
+                break;
+        }
+    }
+}
diff --git a/devTools/javaSanityCheck/src/StringSearchTree.java b/devTools/javaSanityCheck/src/StringSearchTree.java
new file mode 100644
index 00000000000..b1458b397bf
--- /dev/null
+++ b/devTools/javaSanityCheck/src/StringSearchTree.java
@@ -0,0 +1,47 @@
+package org.arkerthan.sanityCheck;
+
+import java.util.List;
+
+public class StringSearchTree {
+    private static final int SIZE = 128;
+    private String element = null;
+    private StringSearchTree[] branches;
+
+    private StringSearchTree() {
+        branches = new StringSearchTree[SIZE];
+    }
+
+
+    public static StringSearchTree generateTree(List<String> list) {
+        StringSearchTree tree = new StringSearchTree();
+
+        for (String s :
+                list) {
+            tree.add(s, 0);
+        }
+
+        return tree;
+    }
+
+    private void add(String s, int index) {
+        if (s.length() == index) {
+            element = s;
+        } else {
+            char c = s.charAt(index);
+            if (branches[c] == null) {
+                branches[c] = new StringSearchTree();
+            }
+            branches[c].add(s, index + 1);
+        }
+    }
+
+    public StringSearchTree getBranch(char c) {
+        if (c >= SIZE) return null;
+        return branches[c];
+    }
+
+    public String getElement() {
+        return element;
+    }
+
+}
diff --git a/devTools/javaSanityCheck/src/SyntaxError.java b/devTools/javaSanityCheck/src/SyntaxError.java
new file mode 100644
index 00000000000..ed2f1f3da76
--- /dev/null
+++ b/devTools/javaSanityCheck/src/SyntaxError.java
@@ -0,0 +1,40 @@
+package org.arkerthan.sanityCheck;
+
+public class SyntaxError extends Exception {
+    private String file;
+    private int line, position;
+    private String description;
+    private int change;
+    private boolean warning = false;
+
+    public SyntaxError(String description, int change) {
+        this.description = description;
+        this.change = change;
+    }
+
+    public SyntaxError(String description, int change, boolean warning) {
+        this(description, change);
+        this.warning = warning;
+    }
+
+    public void setFile(String file) {
+        this.file = file;
+    }
+
+    public void setLine(int line) {
+        this.line = line;
+    }
+
+    public void setPosition(int position) {
+        this.position = position;
+    }
+
+    public String getError() {
+        String s = warning ? "Warning: " : "Error: ";
+        return s + file + ": " + line + ":" + position + " : "+ description;
+    }
+
+    public int getChange() {
+        return change;
+    }
+}
diff --git a/devTools/javaSanityCheck/src/UnknownState.java b/devTools/javaSanityCheck/src/UnknownState.java
new file mode 100644
index 00000000000..3344f09d8f0
--- /dev/null
+++ b/devTools/javaSanityCheck/src/UnknownState.java
@@ -0,0 +1,8 @@
+package org.arkerthan.sanityCheck;
+
+public class UnknownState extends RuntimeException {
+
+    public UnknownState(int state) {
+        super(String.valueOf(state));
+    }
+}
diff --git a/devTools/javaSanityCheck/src/element/AngleBracketElement.java b/devTools/javaSanityCheck/src/element/AngleBracketElement.java
new file mode 100644
index 00000000000..ed669e8c1cb
--- /dev/null
+++ b/devTools/javaSanityCheck/src/element/AngleBracketElement.java
@@ -0,0 +1,515 @@
+package org.arkerthan.sanityCheck.element;
+
+import org.arkerthan.sanityCheck.SyntaxError;
+import org.arkerthan.sanityCheck.UnknownState;
+
+public class AngleBracketElement extends Element {
+    private int state = 0;
+
+    /*
+    -1 - </
+     0 - <
+     1 - <<
+     2 - <<abc
+     3 - <<abc>
+
+     5 - waiting for >
+    -5 - expecting >
+
+    10 - <b
+    br: wait in 5
+
+    15 - <d
+    16 - <di
+    17 - <div
+    18 - <div abc
+   -15 - </d
+   -16 - </di
+   -17 - </div
+
+    works for any number
+    20 - <h
+    21 - <h1 || hr:wait in 5
+   -20 - </h
+   -21 - </h1
+
+    25 - <c
+    26 - <ce
+    27 - <cen
+    28 - <cent
+    29 - <cente
+    center: wait in 5
+    -25 - </c
+    -26 - </ce
+    -27 - </cen
+    -28 - </cent
+    -29 - </cente
+    -30 - </center
+
+     35 - <s
+     36 - <sp
+     37 - <spa
+     span: wait in 5
+     39 - <st
+     40 - <str
+     41 - <stro
+     42 - <stron
+     strong: wait in 5
+     -35 - </s
+     -36 - </sp
+     -37 - </spa
+     span: expecting in -5
+     -39 - </st
+     -40 - </str
+     -41 - </stro
+     -42 - </stron
+     strong: expecting in -5
+   */
+    @Override
+    public int handleChar(char c) throws SyntaxError {
+        switch (state) {
+            case 0:
+                return singleAngleBracket(c);
+            case -1:
+                return closingTag(c);
+            case 1:
+                if (c == '<') {
+                    throw new SyntaxError("Too many \"<\".", 1);
+                } else if (c == '>') {
+                    state = 3;
+                    throw new SyntaxError("Empty Statement?", 1);
+                } else {
+                    state = 2;
+                    return 1;
+                }
+            case 2:
+                //TODO finding special statements: IF, ELSE...
+                if (c == '>') {
+                    state = 3;
+                    return 1;
+                }
+                break;
+            case 3:
+                if (c == '>') {
+                    return 2;
+                } else if (c == ' ' || c == '=') { // assuming comparison
+                    state = 2;
+                } else {
+                    throw new SyntaxError("Closing \">\" missing [1]", 2);
+                }
+                break;
+            case 5:
+                if (c == '>')
+                    return 2;
+                if (c == '@')
+                    return 1;
+                break;
+            case -5:
+                if (c == '>')
+                    return 2;
+                throw new SyntaxError("Closing \">\" missing [2]", 2);
+                //10 - <b
+                //br: wait in 5
+            case 10:
+                if (c == 'r') {
+                    state = 5;
+                    return 1;
+                } else {
+                    state = 1;
+                    throw new SyntaxError("Opening \"<\" missing [1]", 1);
+                }
+                //15 - <d
+                //16 - <di
+                //17 - <div
+                //18 - <div abc
+            case 15:
+                if (c == 'i') {
+                    state = 16;
+                    return 1;
+                } else {
+                    state = 1;
+                    throw new SyntaxError("Opening \"<\" missing.", 1);
+                }
+            case 16:
+                if (c == 'v') {
+                    state = 17;
+                    return 1;
+                } else {
+                    state = 1;
+                    throw new SyntaxError("Opening \"<\" missing.", 1);
+                }
+            case 17:
+                if (c == ' ') {
+                    state = 5;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Missing Space?", 1, true);
+                }
+                //-15 - </d
+                //-16 - </di
+                //-17 - </div
+            case -15:
+                if (c == 'i') {
+                    state = -16;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Unknown Statement:</d" + c, 1, true);
+                }
+            case -16:
+                if (c == 'v') {
+                    state = -17;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Unknown Statement:</di" + c, 1, true);
+                }
+            case -17:
+                if (c == '>') {
+                    return 2;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Unknown Statement:</div" + c, 1, true);
+                }
+                // works for any number
+                // 20 - <h
+                // 21 - <h1 || hr:wait in 5
+                //-20 - </h
+                //-21 - </h1
+            case 20:
+                if (Character.isDigit(c)) {
+                    state = 21;
+                    return 1;
+                } else if (c == 'r') {
+                    state = 5;
+                    return 1;
+                } else {
+                    state = 1;
+                    throw new SyntaxError("Opening \"<\" missing.", 1);
+                }
+            case 21:
+                if (c == '>') {
+                    return 2;
+                } else {
+                    throw new SyntaxError("Closing \">\" missing", 2);
+                }
+            case -20:
+                if (Character.isDigit(c)) {
+                    state = -21;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Unknown Statement:</h" + c, 1, true);
+                }
+            case -21:
+                if (c == '>') {
+                    return 2;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Unknown Statement:</h?" + c, 1, true);
+                }
+                //25 - <c
+                //26 - <ce
+                //27 - <cen
+                //28 - <cent
+                //29 - <cente
+                //wait in 5
+            case 25:
+                if (c == 'e') {
+                    state = 26;
+                    return 1;
+                } else {
+                    state = 1;
+                    throw new SyntaxError("Opening \"<\" missing.", 1);
+                }
+            case 26:
+                if (c == 'n') {
+                    state = 27;
+                    return 1;
+                } else {
+                    state = 1;
+                    throw new SyntaxError("Opening \"<\" missing.", 1);
+                }
+            case 27:
+                if (c == 't') {
+                    state = 28;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Misspelled \"center\"?", 1);
+                }
+            case 28:
+                if (c == 'e') {
+                    state = 29;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Misspelled \"center\"?", 1);
+                }
+            case 29:
+                if (c == 'r') {
+                    state = 5;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Misspelled \"center\"?", 1);
+                }
+                //-25 - </c
+                //-26 - </ce
+                //-27 - </cen
+                //-28 - </cent
+                //-29 - </cente
+                //-30 - </center
+            case -25:
+                if (c == 'e') {
+                    state = -26;
+                    return 1;
+                } else {
+                    state = 1;
+                    throw new SyntaxError("Unknown Statement:</c" + c, 1, true);
+                }
+            case -26:
+                if (c == 'n') {
+                    state = -27;
+                    return 1;
+                } else {
+                    state = 1;
+                    throw new SyntaxError("Unknown Statement:</h?" + c, 1, true);
+                }
+            case -27:
+                if (c == 't') {
+                    state = -28;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Misspelled \"center\"?", 1);
+                }
+            case -28:
+                if (c == 'e') {
+                    state = -29;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Misspelled \"center\"?", 1);
+                }
+            case -29:
+                if (c == 'r') {
+                    state = -30;
+                    return 1;
+                } else {
+                    state = -30;
+                    throw new SyntaxError("Misspelled \"center\"?", 1);
+                }
+            case -30:
+                if (c == '>') {
+                    return 2;
+                } else {
+                    throw new SyntaxError("Closing \">\" missing", 2);
+                }
+                //35 - <s
+                //36 - <sp
+                //37 - <spa
+                //span: wait in 5
+                //39 - <st
+                //40 - <str
+                //41 - <stro
+                //42 - <stron
+                //strong: wait in 5
+            case 35:
+                if (c == 'p') {
+                    state = 36;
+                    return 1;
+                } else if (c == 't') {
+                    state = 39;
+                    return 1;
+                } else {
+                    state = 1;
+                    throw new SyntaxError("Opening \"<\" missing.", 1);
+                }
+            case 36:
+                if (c == 'a') {
+                    state = 37;
+                    return 1;
+                } else {
+                    state = 1;
+                    throw new SyntaxError("Opening \"<\" missing.", 1);
+                }
+            case 37:
+                if (c == 'n') {
+                    state = 5;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Misspelled \"span\"?", 1);
+                }
+            case 39:
+                if (c == 'r') {
+                    state = 40;
+                    return 1;
+                } else {
+                    state = 1;
+                    throw new SyntaxError("Opening \"<\" missing.", 1);
+                }
+            case 40:
+                if (c == 'o') {
+                    state = 41;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Misspelled \"strong\"?", 1);
+                }
+            case 41:
+                if (c == 'n') {
+                    state = 42;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Misspelled \"strong\"?", 1);
+                }
+            case 42:
+                if (c == 'g') {
+                    state = 5;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Misspelled \"strong\"?", 1);
+                }
+                //-35 - </s
+                //-36 - </sp
+                //-37 - </spa
+                //span: expecting in -5
+                //-39 - </st
+                //-40 - </str
+                //-41 - </stro
+                //-42 - </stron
+                //strong: expecting in -5
+            case -35:
+                if (c == 'p') {
+                    state = -36;
+                    return 1;
+                } else if (c == 't') {
+                    state = -39;
+                    return 1;
+                } else {
+                    state = 1;
+                    throw new SyntaxError("Unknown Statement:</s" + c, 1, true);
+                }
+            case -36:
+                if (c == 'a') {
+                    state = -37;
+                    return 1;
+                } else {
+                    state = 1;
+                    throw new SyntaxError("Unknown Statement:</sp" + c, 1, true);
+                }
+            case -37:
+                if (c == 'n') {
+                    state = -5;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Misspelled \"span\"?", 1);
+                }
+            case -39:
+                if (c == 'r') {
+                    state = -40;
+                    return 1;
+                } else {
+                    state = 1;
+                    throw new SyntaxError("Unknown Statement:</sp" + c, 1, true);
+                }
+            case -40:
+                if (c == 'o') {
+                    state = -41;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Misspelled \"strong\"?", 1);
+                }
+            case -41:
+                if (c == 'n') {
+                    state = -42;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Misspelled \"strong\"?", 1);
+                }
+            case -42:
+                if (c == 'g') {
+                    state = -5;
+                    return 1;
+                } else {
+                    state = 5;
+                    throw new SyntaxError("Misspelled \"strong\"?", 1);
+                }
+
+            default:
+                throw new UnknownState(state);
+        }
+        return 0;
+    }
+
+    private int singleAngleBracket(char c) throws SyntaxError {
+        switch (c) {
+            case '<':
+                state = 1;
+                return 1;
+            case '/':
+                state = -1;
+                return 1;
+            case ' ':// assume comparison
+            case '=':// "
+                return 2;
+            case '3'://a heart: <3
+                return 2;
+            case 'a':
+                state = 5;
+                return 1;
+            case 'b':
+                state = 10;
+                return 1;
+            case 'd':
+                state = 15;
+                return 1;
+            case 'h':
+                state = 20;
+                return 1;
+            case 'c':
+                state = 25;
+                return 1;
+            case 's':
+                state = 35;
+                return 1;
+            default:
+                state = 1;
+                throw new SyntaxError("Opening \"<\" missing, found " + c + " [debug:initialCase]", 1);
+        }
+    }
+
+    private int closingTag(char c) throws SyntaxError {
+        switch (c) {
+            case '>':
+                throw new SyntaxError("Empty Statement?", 1, true);
+            case 'a':
+                state = -5;
+                return 1;
+            case 'd':
+                state = -15;
+                return 1;
+            case 'h':
+                state = -20;
+                return 1;
+            case 'c':
+                state = -25;
+                return 1;
+            case 's':
+                state = 35;
+                return 1;
+            default:
+                state = 5;
+                throw new SyntaxError("Unknown Statement", 1, true);
+        }
+    }
+}
+
diff --git a/devTools/javaSanityCheck/src/element/AngleBracketElement2.java b/devTools/javaSanityCheck/src/element/AngleBracketElement2.java
new file mode 100644
index 00000000000..018cb5e75e9
--- /dev/null
+++ b/devTools/javaSanityCheck/src/element/AngleBracketElement2.java
@@ -0,0 +1,147 @@
+package org.arkerthan.sanityCheck.element;
+
+import org.arkerthan.sanityCheck.Main;
+import org.arkerthan.sanityCheck.StringSearchTree;
+import org.arkerthan.sanityCheck.SyntaxError;
+import org.arkerthan.sanityCheck.UnknownState;
+
+public class AngleBracketElement2 extends Element {
+    private int state = 0;
+    private StringSearchTree tree;
+
+    /*
+    -1 - </
+     0 - initial: <
+     1 - <<
+     2 - <<abc
+     3 - <<abc>
+
+     4 - trying to complete HTML tag: <tag ???>
+    -4 - trying to complete HTML tag: </tag>
+     5 - waiting for >
+    -5 - expecting >
+     */
+
+    @Override
+    public int handleChar(char c) throws SyntaxError {
+        switch (state) {
+            case 0:
+                switch (c) {
+                    case '<':
+                        state = 1;
+                        return 1;
+                    case '>':
+                        throw new SyntaxError("Empty Statement?", 2);
+                    case '/':
+                        state = -1;
+                        return 1;
+                    case ' ':// assume comparison
+                    case '=':// "
+                        return 2;
+                    case '3'://a heart: <3
+                        return 2;
+                    default:
+                        try {
+                            state = 4;
+                            tree = Main.htmlTags;
+                            return handleOpeningHTML(c);
+                        } catch (SyntaxError e) {
+                            state = 1;
+                            throw new SyntaxError("Opening \"<\" missing, found " + c + " [debug:initialCase]", 1);
+                        }
+                }
+            case -1:
+                if (c == '>') {
+                    throw new SyntaxError("Empty Statement?", 2, true);
+                }
+                state = -4;
+                tree = Main.htmlTags;
+                return handleClosingHTML(c);
+            case 1:
+                if (c == '<') {
+                    throw new SyntaxError("Too many \"<\".", 1);
+                } else if (c == '>') {
+                    state = 3;
+                    throw new SyntaxError("Empty Statement?", 1);
+                } else {
+                    state = 2;
+                    return 1;
+                }
+            case 2:
+                //TODO finding special Twine statements: IF, ELSE...
+                if (c == '>') {
+                    state = 3;
+                    return 1;
+                }
+                break;
+            case 3:
+                if (c == '>') {
+                    return 2;
+                } else if (c == ' ' || c == '=') { // assuming comparison
+                    state = 2;
+                } else {
+                    throw new SyntaxError("Closing \">\" missing [1]", 2);
+                }
+                break;
+
+            case 4:
+                return handleOpeningHTML(c);
+            case -4:
+                return handleClosingHTML(c);
+            case 5:
+                if (c == '>')
+                    return 2;
+                if (c == '@') //@ inside HTML tags is allowed
+                    return 1;
+                break;
+            case -5:
+                if (c == '>')
+                    return 2;
+                throw new SyntaxError("Closing \">\" missing [2]", 2);
+            default:
+                throw new UnknownState(state);
+        }
+        return 0;
+    }
+
+    private int handleOpeningHTML(char c) throws SyntaxError {
+        if (c == ' ') {
+            state = 5;
+            if (tree.getElement() == null) {
+                throw new SyntaxError("Unknown HTML tag", 1);
+            }
+            return 1;
+        }
+        if (c == '>') {
+            if (tree.getElement() == null) {
+                throw new SyntaxError("Unknown HTML tag", 2);
+            }
+            return 2;
+        }
+
+        tree = tree.getBranch(c);
+        if (tree == null) {
+            state = 5;
+            throw new SyntaxError("Unknown HTML tag or closing \">\" missing, found "+c, 1);
+        }
+
+        return 1;
+    }
+
+    private int handleClosingHTML(char c) throws SyntaxError {
+        if (c == '>') {
+            if (tree.getElement() == null) {
+                throw new SyntaxError("Unknown HTML tag", 2);
+            }
+            return 2;
+        }
+
+        tree = tree.getBranch(c);
+        if (tree == null) {
+            state = -5;
+            throw new SyntaxError("Unknown HTML tag or closing \">\" missing, found "+c, 1);
+        }
+
+        return 1;
+    }
+}
diff --git a/devTools/javaSanityCheck/src/element/AtElement.java b/devTools/javaSanityCheck/src/element/AtElement.java
new file mode 100644
index 00000000000..cedf439542f
--- /dev/null
+++ b/devTools/javaSanityCheck/src/element/AtElement.java
@@ -0,0 +1,72 @@
+package org.arkerthan.sanityCheck.element;
+
+import org.arkerthan.sanityCheck.SyntaxError;
+import org.arkerthan.sanityCheck.UnknownState;
+
+public class AtElement extends Element {
+    private int state = 0;
+    // 0 = @
+    // 1 = @@
+    // 2 = @@.
+    // 3 = @@.a -- @@.ab -- @@.abc
+    // 4 = @@.abc;abc
+    // 5 = @@.abc;abc@
+
+    // example: @@.red;some text@@
+
+    @Override
+    public int handleChar(char c) throws SyntaxError {
+        switch (state) {
+            case 0:
+                state = 1;
+                if (c == '@') {
+                    return 1;
+                } else {
+                    if (c == '.') {
+                        state = 2;
+                    }
+                    throw new SyntaxError("Opening \"@\" missing.", 1);
+                }
+            case 1:
+                if (c == '.') {
+                    state = 2;
+                    return 1;
+                } else {
+                    state = 4;
+                    throw new SyntaxError("\".\" missing, found \"" + c + "\". This might also indicate a " +
+                            "missing closure in the previous color code.", 0, true);
+                }
+            case 2:
+                state = 3;
+                if (Character.isAlphabetic(c)) {
+                    return 1;
+                } else {
+                    throw new SyntaxError("Identifier might be wrong.", 1, true);
+                }
+            case 3:
+                if (c == ';') {
+                    state = 4;
+                    return 1;
+                } else if (c == ' ') {
+                    state = 4;
+                    throw new SyntaxError("\";\" missing or wrong space.", 1);
+                }
+                break;
+            case 4:
+                if (c == '@') {
+                    state = 5;
+                    return 1;
+                }
+                break;
+            case 5:
+                if (c == '@') {
+                    return 2;
+                } else {
+                    throw new SyntaxError("Closing \"@\" missing.", 2);
+                }
+            default:
+                throw new UnknownState(state);
+        }
+        return 0;
+    }
+}
diff --git a/devTools/javaSanityCheck/src/element/Element.java b/devTools/javaSanityCheck/src/element/Element.java
new file mode 100644
index 00000000000..ed289d2decf
--- /dev/null
+++ b/devTools/javaSanityCheck/src/element/Element.java
@@ -0,0 +1,17 @@
+package org.arkerthan.sanityCheck.element;
+
+import org.arkerthan.sanityCheck.SyntaxError;
+
+public abstract class Element {
+
+    /**
+     * Parses a Char and returns an int depending on the state of the element
+     * 0 - the Element did nothing
+     * 1 - the Element changed state
+     * 2 - the Element is finished
+     * @param c
+     * @return
+     * @throws Error
+     */
+    public abstract int handleChar(char c) throws SyntaxError;
+}
diff --git a/devTools/javaSanityCheck/src/element/KnownElement.java b/devTools/javaSanityCheck/src/element/KnownElement.java
new file mode 100644
index 00000000000..34390b7ef3e
--- /dev/null
+++ b/devTools/javaSanityCheck/src/element/KnownElement.java
@@ -0,0 +1,44 @@
+package org.arkerthan.sanityCheck.element;
+
+import org.arkerthan.sanityCheck.SyntaxError;
+
+public class KnownElement extends Element {
+
+    private boolean opening;
+    private String statement;
+
+    public KnownElement(boolean opening, String statement) {
+        this.opening = opening;
+        this.statement = statement;
+    }
+
+    @Override
+    public int handleChar(char c) throws SyntaxError {
+        return 0;
+    }
+
+    /**
+     * @return true, if it needs another Known Element to close it.
+     */
+    public boolean isOpening() {
+        return opening;
+    }
+
+    /**
+     * @param k Element to be checked
+     * @return true if given Element closes Element
+     */
+    public boolean isClosingElement(KnownElement k) {
+        return k.statement.equals(this.statement);
+    }
+
+    /**
+     * Returns, if isOpening is true, the needed statement for closing,
+     * otherwise the statement that generated it.
+     *
+     * @return statement
+     */
+    public String getStatement() {
+        return statement;
+    }
+}
diff --git a/sanityCheck-java b/sanityCheck-java
new file mode 100755
index 00000000000..c564d01dfc6
--- /dev/null
+++ b/sanityCheck-java
@@ -0,0 +1 @@
+git ls-files "src/*.tw" | xargs java -jar SanityCheck.jar
-- 
GitLab