From 68100c92bca7c168929b3fba0f300f9297fc408d Mon Sep 17 00:00:00 2001
From: Arkerthan <arkerthan@gmail.com>
Date: Mon, 11 Mar 2019 19:45:09 +0100
Subject: [PATCH] updated sources; maybe usable on win as well; not dependend
 on git

---
 SanityCheck.jar                               | Bin 14909 -> 15448 bytes
 devTools/javaSanityCheck/src/Main.java        | 121 ++++++++++++++----
 .../javaSanityCheck/src/StringSearchTree.java |  47 -------
 devTools/javaSanityCheck/src/SyntaxError.java |   2 +-
 .../javaSanityCheck/src/TagSearchTree.java    |  50 ++++++++
 .../javaSanityCheck/src/UnknownState.java     |   8 --
 .../src/UnknownStateException.java            |   8 ++
 .../src/element/AngleBracketElement.java      | 100 ++++++++++++---
 .../src/element/AtElement.java                |  37 +++++-
 .../javaSanityCheck/src/element/Element.java  |  24 ++++
 ...nownElement.java => KnownHtmlElement.java} |  23 +++-
 devTools/javaSanityCheck/src/tag/HtmlTag.java |   7 +
 devTools/javaSanityCheck/src/tag/Tag.java     |  11 ++
 sanityCheck-java                              |   2 +-
 sanityCheck-java.bat                          |   1 +
 15 files changed, 331 insertions(+), 110 deletions(-)
 delete mode 100644 devTools/javaSanityCheck/src/StringSearchTree.java
 create mode 100644 devTools/javaSanityCheck/src/TagSearchTree.java
 delete mode 100644 devTools/javaSanityCheck/src/UnknownState.java
 create mode 100644 devTools/javaSanityCheck/src/UnknownStateException.java
 rename devTools/javaSanityCheck/src/element/{KnownElement.java => KnownHtmlElement.java} (56%)
 create mode 100644 devTools/javaSanityCheck/src/tag/HtmlTag.java
 create mode 100644 devTools/javaSanityCheck/src/tag/Tag.java
 create mode 100644 sanityCheck-java.bat

diff --git a/SanityCheck.jar b/SanityCheck.jar
index 1cb9790d50fd09037c2cbb81229ed229ad171df0..bfde3d7a88d36b4a699b06f936bf3abd37a3d929 100644
GIT binary patch
delta 5130
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

delta 4599
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

diff --git a/devTools/javaSanityCheck/src/Main.java b/devTools/javaSanityCheck/src/Main.java
index 776e4430c37..024f1626d4c 100644
--- a/devTools/javaSanityCheck/src/Main.java
+++ b/devTools/javaSanityCheck/src/Main.java
@@ -3,10 +3,15 @@ package org.arkerthan.sanityCheck;
 import org.arkerthan.sanityCheck.element.AngleBracketElement;
 import org.arkerthan.sanityCheck.element.AtElement;
 import org.arkerthan.sanityCheck.element.Element;
+import org.arkerthan.sanityCheck.element.KnownHtmlElement;
+import org.arkerthan.sanityCheck.tag.HtmlTag;
+import org.arkerthan.sanityCheck.tag.Tag;
 
 import java.io.*;
 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.LinkedList;
 import java.util.List;
@@ -14,7 +19,7 @@ import java.util.Stack;
 
 public class Main {
 
-    public static StringSearchTree htmlTags;
+    public static TagSearchTree<HtmlTag> htmlTags;
     private static String currentFile;
     private static int currentLine, currentPosition;
     private static Stack<Element> stack;
@@ -24,22 +29,8 @@ public class Main {
     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);
-                }
-            }
-        }
+        Path workingDir = Paths.get("").toAbsolutePath();
+        runSanityCheckInDirectory(workingDir, new File("src/"));
 
         //handle errors
         for (SyntaxError e :
@@ -48,26 +39,76 @@ public class Main {
         }
     }
 
+
+    /**
+     * Goes through the whole directory including subdirectories and runs
+     * sanitycheck() on all .tw files
+     *
+     * @param dir to be checked
+     */
+    private static void runSanityCheckInDirectory(Path workingDir, File dir) {
+
+        //get all the files from a directory
+        try {
+            for (File file : dir.listFiles()) {
+                if (file.isFile()) {
+                    String path = file.getAbsolutePath();
+                    if (path.endsWith(".tw")) {
+                        sanityCheck(workingDir.relativize(file.toPath()));
+                    }
+                } else if (file.isDirectory()) {
+                    runSanityCheckInDirectory(workingDir, file.getAbsoluteFile());
+                }
+            }
+        } catch (NullPointerException e) {
+            System.err.println("Couldn't find directory " + dir.getPath());
+        }
+    }
+
+    private static void sanityCheck(Path path) {
+        File file = path.toFile();
+
+        // replace this with a known encoding if possible
+        Charset encoding = Charset.defaultCharset();
+
+        if (!excluded(file.getPath())) {
+            try {
+                handleFile(file, encoding);
+            } catch (IOException e) {
+                System.err.println("Couldn't read " + file);
+            }
+        }
+    }
+
+
     private static void setupHtmlTags() {
         //preparing excluding folders
-        List<String> htmlTagsList = new ArrayList<>();
+        List<Tag> htmlTagsList = new ArrayList<>();
         try {
 
             Files.lines(new File("devTools/javaSanityCheck/htmlTags").toPath()).map(String::trim)
                     .filter(s -> !s.startsWith("#"))
-                    .forEach(htmlTagsList::add);
+                    .forEach(s -> htmlTagsList.add(parseTag(s)));
         } catch (IOException e) {
             System.err.println("Couldn't read devTools/javaSanityCheck/htmlTags");
         }
 
         try {
-            htmlTags = StringSearchTree.generateTree(htmlTagsList);
+            htmlTags = new TagSearchTree(htmlTagsList);
         } catch (ArrayIndexOutOfBoundsException e) {
             System.err.println("Illegal Character in devTools/javaSanityCheck/htmlTags");
             System.exit(-1);
         }
     }
 
+    private static HtmlTag parseTag(String s) {
+        String[] st = s.split(";");
+        if (st.length > 1 && st[1].equals("1")) {
+            return new HtmlTag(st[0], false);
+        }
+        return new HtmlTag(st[0], true);
+    }
+
     private static void setupExclude() {
         //preparing excluding folders
         List<String> excludedList = new ArrayList<>();
@@ -91,8 +132,11 @@ public class Main {
         return false;
     }
 
-
     private static void handleFile(File file, Charset encoding) throws IOException {
+        currentFile = file.getPath();
+        currentLine = 1;
+        stack = new Stack<>();
+
         try (InputStream in = new FileInputStream(file);
              Reader reader = new InputStreamReader(in, encoding);
              // buffer for efficiency
@@ -122,28 +166,51 @@ public class Main {
             try {
                 change = stack.peek().handleChar(c);
             } catch (SyntaxError e) {
-                e.setFile(currentFile);
-                e.setLine(currentLine);
-                e.setPosition(currentPosition);
                 change = e.getChange();
-                errors.add(e);
+                addError(e);
             }
             if (change > 0) {
                 if (change == 2) {
                     stack.pop();
                 }
+                if (change == 3) {
+                    KnownHtmlElement k = stack.pop().getKnownElement();
+                    if (k.isOpening()) {
+                        stack.push(k);
+                    } else if (stack.empty()) {
+                        addError(new SyntaxError("Closed HTML tag \"" + k.getStatement() + "\" without having any open tags.", -1));
+                    } else if (stack.peek() instanceof KnownHtmlElement) {
+                        KnownHtmlElement kFirst = (KnownHtmlElement) stack.peek();
+                        if (!kFirst.isMatchingElement(k)) {
+                            addError(new SyntaxError("Opening HTML tag \"" + kFirst.getStatement() +
+                                    "\" does not match closing tag \"" + k.getStatement() + "\".", -1));
+                        }
+                        stack.pop();
+                    } else {
+                        addError(new SyntaxError("Closing HTML tag \"" + k.getStatement() + "\" inside " +
+                                "another tag: " + stack.peek().getShortDescription(), -1, true));
+                    }
+                }
                 return;
             }
         }
 
+
         //innermost element was uninterested, trying to find matching element
         switch (c) {
             case '@':
-                stack.push(new AtElement());
+                stack.push(new AtElement(currentLine, currentPosition));
                 break;
             case '<':
-                stack.push(new AngleBracketElement());
+                stack.push(new AngleBracketElement(currentLine, currentPosition));
                 break;
         }
     }
+
+    private static void addError(SyntaxError e) {
+        e.setFile(currentFile);
+        e.setLine(currentLine);
+        e.setPosition(currentPosition);
+        errors.add(e);
+    }
 }
diff --git a/devTools/javaSanityCheck/src/StringSearchTree.java b/devTools/javaSanityCheck/src/StringSearchTree.java
deleted file mode 100644
index b1458b397bf..00000000000
--- a/devTools/javaSanityCheck/src/StringSearchTree.java
+++ /dev/null
@@ -1,47 +0,0 @@
-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
index ed2f1f3da76..90d9db7333c 100644
--- a/devTools/javaSanityCheck/src/SyntaxError.java
+++ b/devTools/javaSanityCheck/src/SyntaxError.java
@@ -4,7 +4,7 @@ public class SyntaxError extends Exception {
     private String file;
     private int line, position;
     private String description;
-    private int change;
+    private int change; //see Element for values; -1 means not thrown
     private boolean warning = false;
 
     public SyntaxError(String description, int change) {
diff --git a/devTools/javaSanityCheck/src/TagSearchTree.java b/devTools/javaSanityCheck/src/TagSearchTree.java
new file mode 100644
index 00000000000..567f33f35d7
--- /dev/null
+++ b/devTools/javaSanityCheck/src/TagSearchTree.java
@@ -0,0 +1,50 @@
+package org.arkerthan.sanityCheck;
+
+import org.arkerthan.sanityCheck.tag.Tag;
+
+import java.util.List;
+
+public class TagSearchTree<E extends Tag> {
+    private static final int SIZE = 128;
+    private E element = null;
+    private TagSearchTree<E>[] branches;
+    private String path;
+
+    private TagSearchTree() {
+        branches = new TagSearchTree[SIZE];
+    }
+
+    public TagSearchTree(List<E> list) {
+        this();
+        for (E e :
+                list) {
+            this.add(e, 0);
+        }
+    }
+
+    private void add(E e, int index) {
+        path = e.tag.substring(0,index);
+        if (e.tag.length() == index) {
+            element = e;
+        } else {
+            char c = e.tag.charAt(index);
+            if (branches[c] == null) {
+                branches[c] = new TagSearchTree<>();
+            }
+            branches[c].add(e, index + 1);
+        }
+    }
+
+    public TagSearchTree<E> getBranch(char c) {
+        if (c >= SIZE) return null;
+        return branches[c];
+    }
+
+    public E getElement() {
+        return element;
+    }
+
+    public String getPath() {
+        return path;
+    }
+}
diff --git a/devTools/javaSanityCheck/src/UnknownState.java b/devTools/javaSanityCheck/src/UnknownState.java
deleted file mode 100644
index 3344f09d8f0..00000000000
--- a/devTools/javaSanityCheck/src/UnknownState.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package org.arkerthan.sanityCheck;
-
-public class UnknownState extends RuntimeException {
-
-    public UnknownState(int state) {
-        super(String.valueOf(state));
-    }
-}
diff --git a/devTools/javaSanityCheck/src/UnknownStateException.java b/devTools/javaSanityCheck/src/UnknownStateException.java
new file mode 100644
index 00000000000..dceb35bc3d5
--- /dev/null
+++ b/devTools/javaSanityCheck/src/UnknownStateException.java
@@ -0,0 +1,8 @@
+package org.arkerthan.sanityCheck;
+
+public class UnknownStateException extends RuntimeException {
+
+    public UnknownStateException(int state) {
+        super(String.valueOf(state));
+    }
+}
diff --git a/devTools/javaSanityCheck/src/element/AngleBracketElement.java b/devTools/javaSanityCheck/src/element/AngleBracketElement.java
index 3453835add6..35877dec434 100644
--- a/devTools/javaSanityCheck/src/element/AngleBracketElement.java
+++ b/devTools/javaSanityCheck/src/element/AngleBracketElement.java
@@ -1,14 +1,13 @@
 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;
+import org.arkerthan.sanityCheck.TagSearchTree;
+import org.arkerthan.sanityCheck.UnknownStateException;
+import org.arkerthan.sanityCheck.tag.HtmlTag;
 
 public class AngleBracketElement extends Element {
     private int state = 0;
-    private StringSearchTree tree;
-
     /*
     -1 - </
      0 - initial: <
@@ -20,8 +19,15 @@ public class AngleBracketElement extends Element {
     -4 - trying to complete HTML tag: </tag>
      5 - waiting for >
     -5 - expecting >
+     6 - waiting for > with KnownHtmlElement
      */
 
+    private TagSearchTree<HtmlTag> htmlTree;
+
+    public AngleBracketElement(int line, int pos) {
+        super(line, pos);
+    }
+
     @Override
     public int handleChar(char c) throws SyntaxError {
         switch (state) {
@@ -43,7 +49,7 @@ public class AngleBracketElement extends Element {
                     default:
                         try {
                             state = 4;
-                            tree = Main.htmlTags;
+                            htmlTree = Main.htmlTags;
                             return handleOpeningHTML(c);
                         } catch (SyntaxError e) {
                             state = 1;
@@ -55,7 +61,7 @@ public class AngleBracketElement extends Element {
                     throw new SyntaxError("Empty Statement?", 2, true);
                 }
                 state = -4;
-                tree = Main.htmlTags;
+                htmlTree = Main.htmlTags;
                 return handleClosingHTML(c);
             case 1:
                 if (c == '<') {
@@ -98,8 +104,14 @@ public class AngleBracketElement extends Element {
                 if (c == '>')
                     return 2;
                 throw new SyntaxError("Closing \">\" missing [2]", 2);
+            case 6:
+                if (c == '>')
+                    return 3;
+                if (c == '@') //@ inside HTML tags is allowed
+                    return 1;
+                break;
             default:
-                throw new UnknownState(state);
+                throw new UnknownStateException(state);
         }
         return 0;
     }
@@ -107,22 +119,31 @@ public class AngleBracketElement extends Element {
     private int handleOpeningHTML(char c) throws SyntaxError {
         if (c == ' ') {
             state = 5;
-            if (tree.getElement() == null) {
+            if (htmlTree.getElement() == null) {
                 throw new SyntaxError("Unknown HTML tag", 1);
             }
+            if (!htmlTree.getElement().single) {
+                k = new KnownHtmlElement(line, pos, true, htmlTree.getElement().tag);
+                state = 6;
+                return 1;
+            }
             return 1;
         }
         if (c == '>') {
-            if (tree.getElement() == null) {
+            if (htmlTree.getElement() == null) {
                 throw new SyntaxError("Unknown HTML tag", 2);
             }
+            if (!htmlTree.getElement().single) {
+                k = new KnownHtmlElement(line, pos, true, htmlTree.getElement().tag);
+                return 3;
+            }
             return 2;
         }
 
-        tree = tree.getBranch(c);
-        if (tree == null) {
+        htmlTree = htmlTree.getBranch(c);
+        if (htmlTree == null) {
             state = 5;
-            throw new SyntaxError("Unknown HTML tag or closing \">\" missing, found "+c, 1);
+            throw new SyntaxError("Unknown HTML tag or closing \">\" missing, found " + c, 1);
         }
 
         return 1;
@@ -130,18 +151,63 @@ public class AngleBracketElement extends Element {
 
     private int handleClosingHTML(char c) throws SyntaxError {
         if (c == '>') {
-            if (tree.getElement() == null) {
+            if (htmlTree.getElement() == null) {
                 throw new SyntaxError("Unknown HTML tag", 2);
             }
-            return 2;
+            if (htmlTree.getElement().single) {
+                throw new SyntaxError("Single HTML tag used as closing Tag: " + htmlTree.getElement().tag, 2);
+            }
+            k = new KnownHtmlElement(line, pos, false, htmlTree.getElement().tag);
+            return 3;
         }
 
-        tree = tree.getBranch(c);
-        if (tree == null) {
+        htmlTree = htmlTree.getBranch(c);
+        if (htmlTree == null) {
             state = -5;
-            throw new SyntaxError("Unknown HTML tag or closing \">\" missing, found "+c, 1);
+            throw new SyntaxError("Unknown HTML tag or closing \">\" missing, found " + c, 1);
         }
 
         return 1;
     }
+
+    @Override
+    public String getShortDescription() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(line).append(":").append(pos).append(" ");
+        switch (state) {
+            case 0:
+                builder.append("<");
+                break;
+            case 1:
+                builder.append("<<");
+                break;
+            case -1:
+                builder.append("</");
+                break;
+            case 2:
+                builder.append("<<???");
+                break;
+            case 3:
+                builder.append("<<???>");
+                break;
+            case 4:
+                builder.append("<").append(htmlTree.getPath());
+                break;
+            case -4:
+                builder.append("</").append(htmlTree.getPath());
+                break;
+            case 5:
+                builder.append("<").append(htmlTree.getPath()).append(" ???");
+                break;
+            case -5:
+                builder.append("</").append(htmlTree.getPath());
+                break;
+            case 6:
+                builder.append("<").append(htmlTree.getPath()).append(" ???");
+                break;
+            default:
+                throw new UnknownStateException(state);
+        }
+        return builder.toString();
+    }
 }
diff --git a/devTools/javaSanityCheck/src/element/AtElement.java b/devTools/javaSanityCheck/src/element/AtElement.java
index cedf439542f..0dad04247c5 100644
--- a/devTools/javaSanityCheck/src/element/AtElement.java
+++ b/devTools/javaSanityCheck/src/element/AtElement.java
@@ -1,7 +1,7 @@
 package org.arkerthan.sanityCheck.element;
 
 import org.arkerthan.sanityCheck.SyntaxError;
-import org.arkerthan.sanityCheck.UnknownState;
+import org.arkerthan.sanityCheck.UnknownStateException;
 
 public class AtElement extends Element {
     private int state = 0;
@@ -14,6 +14,10 @@ public class AtElement extends Element {
 
     // example: @@.red;some text@@
 
+    public AtElement(int line, int pos) {
+        super(line, pos);
+    }
+
     @Override
     public int handleChar(char c) throws SyntaxError {
         switch (state) {
@@ -65,8 +69,37 @@ public class AtElement extends Element {
                     throw new SyntaxError("Closing \"@\" missing.", 2);
                 }
             default:
-                throw new UnknownState(state);
+                throw new UnknownStateException(state);
         }
         return 0;
     }
+
+    @Override
+    public String getShortDescription() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(line).append(":").append(pos).append(" ");
+        switch (state) {
+            case 0:
+                builder.append("@");
+                break;
+            case 1:
+                builder.append("@@");
+                break;
+            case 2:
+                builder.append("@@.");
+                break;
+            case 3:
+                builder.append("@@.???");
+                break;
+            case 4:
+                builder.append("@@???");
+                break;
+            case 5:
+                builder.append("@@???@");
+                break;
+            default:
+                throw new UnknownStateException(state);
+        }
+        return builder.toString();
+    }
 }
diff --git a/devTools/javaSanityCheck/src/element/Element.java b/devTools/javaSanityCheck/src/element/Element.java
index ed289d2decf..53934268618 100644
--- a/devTools/javaSanityCheck/src/element/Element.java
+++ b/devTools/javaSanityCheck/src/element/Element.java
@@ -3,15 +3,39 @@ package org.arkerthan.sanityCheck.element;
 import org.arkerthan.sanityCheck.SyntaxError;
 
 public abstract class Element {
+    protected KnownHtmlElement k;
+    protected int line, pos;
+
+    /**
+     *
+     * @param line Line the instance was created
+     * @param pos Position in line the instance was created
+     */
+    protected Element(int line, int pos) {
+        this.line = line;
+        this.pos = pos;
+    }
 
     /**
      * 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
+     * 3 - the Element is finished and a KnownHtmlElement was generated
+     *
      * @param c
      * @return
      * @throws Error
      */
     public abstract int handleChar(char c) throws SyntaxError;
+
+    public KnownHtmlElement getKnownElement() {
+        return k;
+    }
+
+    /**
+     *
+     * @return a short description usually based on state and position of the Element
+     */
+    public abstract String getShortDescription();
 }
diff --git a/devTools/javaSanityCheck/src/element/KnownElement.java b/devTools/javaSanityCheck/src/element/KnownHtmlElement.java
similarity index 56%
rename from devTools/javaSanityCheck/src/element/KnownElement.java
rename to devTools/javaSanityCheck/src/element/KnownHtmlElement.java
index 34390b7ef3e..b3b13673378 100644
--- a/devTools/javaSanityCheck/src/element/KnownElement.java
+++ b/devTools/javaSanityCheck/src/element/KnownHtmlElement.java
@@ -1,24 +1,33 @@
 package org.arkerthan.sanityCheck.element;
 
-import org.arkerthan.sanityCheck.SyntaxError;
-
-public class KnownElement extends Element {
+public class KnownHtmlElement extends Element {
 
     private boolean opening;
     private String statement;
 
-    public KnownElement(boolean opening, String statement) {
+    public KnownHtmlElement(int line, int pos, boolean opening, String statement) {
+        super(line, pos);
         this.opening = opening;
         this.statement = statement;
     }
 
     @Override
-    public int handleChar(char c) throws SyntaxError {
+    public int handleChar(char c) {
         return 0;
     }
 
+    @Override
+    public String getShortDescription() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(line).append(":").append(pos).append(" <");
+        if(!opening){
+            builder.append("/");
+        }
+        return builder.append(statement).toString();
+    }
+
     /**
-     * @return true, if it needs another Known Element to close it.
+     * @return true, if it needs another Known Element to close it, false if it closes another element.
      */
     public boolean isOpening() {
         return opening;
@@ -28,7 +37,7 @@ public class KnownElement extends Element {
      * @param k Element to be checked
      * @return true if given Element closes Element
      */
-    public boolean isClosingElement(KnownElement k) {
+    public boolean isMatchingElement(KnownHtmlElement k) {
         return k.statement.equals(this.statement);
     }
 
diff --git a/devTools/javaSanityCheck/src/tag/HtmlTag.java b/devTools/javaSanityCheck/src/tag/HtmlTag.java
new file mode 100644
index 00000000000..70bff2c5a32
--- /dev/null
+++ b/devTools/javaSanityCheck/src/tag/HtmlTag.java
@@ -0,0 +1,7 @@
+package org.arkerthan.sanityCheck.tag;
+
+public class HtmlTag extends Tag {
+    public HtmlTag(String tag, boolean single) {
+        super(tag, single);
+    }
+}
diff --git a/devTools/javaSanityCheck/src/tag/Tag.java b/devTools/javaSanityCheck/src/tag/Tag.java
new file mode 100644
index 00000000000..386292b48eb
--- /dev/null
+++ b/devTools/javaSanityCheck/src/tag/Tag.java
@@ -0,0 +1,11 @@
+package org.arkerthan.sanityCheck.tag;
+
+public abstract class Tag {
+    public final String tag;
+    public final boolean single;
+
+    protected Tag(String tag, boolean single) {
+        this.tag = tag;
+        this.single = single;
+    }
+}
diff --git a/sanityCheck-java b/sanityCheck-java
index c564d01dfc6..455bbfbc243 100755
--- a/sanityCheck-java
+++ b/sanityCheck-java
@@ -1 +1 @@
-git ls-files "src/*.tw" | xargs java -jar SanityCheck.jar
+java -jar SanityCheck.jar
diff --git a/sanityCheck-java.bat b/sanityCheck-java.bat
new file mode 100644
index 00000000000..455bbfbc243
--- /dev/null
+++ b/sanityCheck-java.bat
@@ -0,0 +1 @@
+java -jar SanityCheck.jar
-- 
GitLab