diff --git a/SanityCheck.jar b/SanityCheck.jar
new file mode 100644
index 0000000000000000000000000000000000000000..42a086d8c043eb99010faa0dfcca5dc778ad0e1d
Binary files /dev/null and b/SanityCheck.jar differ
diff --git a/devTools/javaSanityCheck/excluded b/devTools/javaSanityCheck/excluded
new file mode 100644
index 0000000000000000000000000000000000000000..1b1e091323a74b93ccf8bfc7861906b967c6439e
--- /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 0000000000000000000000000000000000000000..c306acc52dd6141ea7b38abdf51725ce483645b0
--- /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 0000000000000000000000000000000000000000..e1f7909624c79b630cd8c1c6784821c97fff9fe4
--- /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 0000000000000000000000000000000000000000..b1458b397bfc48525c47a9010964ae4165b46c15
--- /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 0000000000000000000000000000000000000000..ed2f1f3da76aa4195050c6f20014e3dc8bfb6259
--- /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 0000000000000000000000000000000000000000..3344f09d8f08bd838c127276321c02a774cd8106
--- /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 0000000000000000000000000000000000000000..ed669e8c1cb7f6726e69364fae2e63c991097e75
--- /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 0000000000000000000000000000000000000000..018cb5e75e90f1f2e97c4465c6a463d91a9a98ac
--- /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 0000000000000000000000000000000000000000..cedf439542f43b2bed0459508f4836ad0a7e0bde
--- /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 0000000000000000000000000000000000000000..ed289d2decf21a6e461b1cbafadf061a7706a4d8
--- /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 0000000000000000000000000000000000000000..34390b7ef3efb04d784a1ccbdfaf86b5edf10e55
--- /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 0000000000000000000000000000000000000000..c564d01dfc659224509727bfa9098774f886dea2
--- /dev/null
+++ b/sanityCheck-java
@@ -0,0 +1 @@
+git ls-files "src/*.tw" | xargs java -jar SanityCheck.jar