From 41d6234a6e011bc1c090305af887312546cd6474 Mon Sep 17 00:00:00 2001 From: Arkerthan <arkerthan@gmail.com> Date: Wed, 20 Mar 2019 00:52:59 +0100 Subject: [PATCH] sanityCheck finished & cleaned --- SanityCheck.jar | Bin 21784 -> 21795 bytes devTools/javaSanityCheck/info.txt | 2 + .../src/DisallowedTagException.java | 9 +- devTools/javaSanityCheck/src/Main.java | 619 +++++++++--------- devTools/javaSanityCheck/src/SyntaxError.java | 109 +-- devTools/javaSanityCheck/src/Tag.java | 15 +- .../javaSanityCheck/src/TagSearchTree.java | 129 ++-- .../src/UnknownStateException.java | 9 +- .../src/element/AngleBracketElement.java | 5 +- .../src/element/AtElement.java | 191 +++--- .../src/element/BracketElement.java | 28 + .../src/element/CommentElement.java | 7 +- .../javaSanityCheck/src/element/Element.java | 78 ++- .../src/element/KnownElement.java | 51 +- .../src/element/KnownHtmlElement.java | 77 +-- .../src/element/KnownLogicElement.java | 205 +++--- .../src/element/KnownTwineElement.java | 78 +-- 17 files changed, 845 insertions(+), 767 deletions(-) create mode 100644 devTools/javaSanityCheck/info.txt create mode 100644 devTools/javaSanityCheck/src/element/BracketElement.java diff --git a/SanityCheck.jar b/SanityCheck.jar index 7193ecd1a4089711f1fcfdc0f0d3a7ca8008a5b1..f19e52f690973f356affb974151449cdce45adcc 100644 GIT binary patch literal 21795 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 B=zstK literal 21784 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 diff --git a/devTools/javaSanityCheck/info.txt b/devTools/javaSanityCheck/info.txt new file mode 100644 index 00000000000..e949bd8d03f --- /dev/null +++ b/devTools/javaSanityCheck/info.txt @@ -0,0 +1,2 @@ +Full sources for easy import into IDE can be found here: +https://gitgud.io/Arkerthan/twine-sanitycheck diff --git a/devTools/javaSanityCheck/src/DisallowedTagException.java b/devTools/javaSanityCheck/src/DisallowedTagException.java index cd479fbdf5f..2dfc4a4f0cd 100644 --- a/devTools/javaSanityCheck/src/DisallowedTagException.java +++ b/devTools/javaSanityCheck/src/DisallowedTagException.java @@ -1,8 +1,11 @@ package org.arkerthan.sanityCheck; +/** + * @author Arkerthan + */ public class DisallowedTagException extends RuntimeException { - public DisallowedTagException(String tag) { - super(tag); - } + public DisallowedTagException(String tag) { + super(tag); + } } diff --git a/devTools/javaSanityCheck/src/Main.java b/devTools/javaSanityCheck/src/Main.java index 84c471d2148..15ed975f268 100644 --- a/devTools/javaSanityCheck/src/Main.java +++ b/devTools/javaSanityCheck/src/Main.java @@ -14,315 +14,318 @@ import java.util.*; /** * @author Arkerthan + * @version 1.0 */ public class Main { - public static TagSearchTree<Tag> htmlTags, twineTags; - 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) { - - //setup - setupExclude(); - setupHtmlTags(); - setupTwineTags(); - Path workingDir = Paths.get("").toAbsolutePath(); - - //actual sanityCheck - runSanityCheckInDirectory(workingDir, new File("src/")); - - //output errors - for (SyntaxError e : - errors) { - System.out.println(e.getError()); - } - } - - - /** - * Goes through the whole directory including subdirectories and runs - * {@link Main#sanityCheck(Path)} on all .tw files - * - * @param dir to be checked - */ - private static void runSanityCheckInDirectory(Path workingDir, File dir) { - //subdirectories are checked recursively - - try { - for (File file : dir.listFiles()) { - if (file.isFile()) { //run sanityCheck if file is a .tw file - String path = file.getAbsolutePath(); - if (path.endsWith(".tw")) { - sanityCheck(workingDir.relativize(file.toPath())); - } - } else if (file.isDirectory()) { - runSanityCheckInDirectory(workingDir, file.getAbsoluteFile()); - } - } - } catch (NullPointerException e) { - e.printStackTrace(); - System.err.println("Couldn't read directory " + currentFile); - System.exit(-1); - } - } - - /** - * Runs the sanity check for one file. Does not run if file is excluded. - * - * @param path file to be checked - */ - 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())) { - currentFile = file.getPath(); - currentLine = 1; - stack = new Stack<>(); - - //actually opening and reading the file - try (InputStream in = new FileInputStream(file); - Reader reader = new InputStreamReader(in, encoding); - // buffer for efficiency - Reader buffer = new BufferedReader(reader)) { - handleCharacters(buffer); - } catch (IOException e) { - e.printStackTrace(); - System.err.println("Couldn't read " + file); - } - } - } - - /** - * sets up a {@link TagSearchTree<Tag>} for fast access of HTML tags later - */ - private static void setupHtmlTags() { - //load HTML tags into a list - List<Tag> TagsList = loadTags("devTools/javaSanityCheck/htmlTags"); - - //turn List into alphabetical search tree - try { - htmlTags = new TagSearchTree<>(TagsList); - } catch (ArrayIndexOutOfBoundsException e) { - System.err.println("Illegal Character in devTools/javaSanityCheck/htmlTags"); - System.exit(-1); - } - } - - /** - * sets up a {@link TagSearchTree<Tag>} for fast access of twine tags later - */ - private static void setupTwineTags() { - //load twine tags into a list - List tagsList = loadTags("devTools/javaSanityCheck/twineTags"); - - //turn List into alphabetical search tree - try { - twineTags = new TagSearchTree<>(tagsList); - } catch (ArrayIndexOutOfBoundsException e) { - System.err.println("Illegal Character in devTools/javaSanityCheck/twineTags"); - System.exit(-1); - } - } - - /** - * Loads a list of tags from a file - * - * @param filePath file to load tags from - * @return loaded tags - */ - private static List<Tag> loadTags(String filePath) { - List<Tag> tagsList = new LinkedList<>(); - try { - Files.lines(new File(filePath).toPath()).map(String::trim) - .filter(s -> !s.startsWith("#")) - .forEach(s -> tagsList.add(parseTag(s))); - } catch (IOException e) { - System.err.println("Couldn't read " + filePath); - } - return tagsList; - } - - /** - * Turns a string into a Tag - * ";1" at the end of the String indicates that the tag needs to be closed later - * - * @param s tag as String - * @return tag as Tag - */ - private static Tag parseTag(String s) { - String[] st = s.split(";"); - if (st.length > 1 && st[1].equals("1")) { - return new Tag(st[0], false); - } - return new Tag(st[0], true); - } - - /** - * sets up the excluded files array. - */ - private static void setupExclude() { - //load excluded files - 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"); - } - - //turn excluded files into an array and change path to windows style if needed - if (isWindows()) { - excluded = new String[excludedList.size()]; - int i = 0; - for (String s : - excludedList) { - excluded[i++] = s.replaceAll("/", "\\\\"); - } - } else { - excluded = excludedList.toArray(new String[0]); - } - } - - /** - * @return whether OS is Windows or not - */ - private static boolean isWindows() { - return (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows")); - } - - /** - * checks if a file or directory is excluded from the sanity check - * - * @param s file/directory to be checked - * @return whether it is excluded or not - */ - private static boolean excluded(String s) { - for (String ex : - excluded) { - if (s.startsWith(ex)) return true; - } - return false; - } - - /** - * Reads the file character by character. - * - * @param reader reader that is read - * @throws IOException thrown if the file can't be read - */ - private static void handleCharacters(Reader reader) throws IOException { - int r; - while ((r = reader.read()) != -1) { - char c = (char) r; - handleCharacter(c); - } - } - - /** - * Handles a single character - * - * @param c next character - */ - private static void handleCharacter(char c) { - //updating position - 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) { - change = e.getChange(); - addError(e); - } - - //change greater 0 means the innermost element did some work - if (change > 0) { - //2 means the Element is complete - if (change == 2) { - //remove the topmost element from stack since it is complete - stack.pop(); - return; - } - //3 means the Element is complete and part of a two or more tag system - if (change == 3) { - //remove the topmost element from stack since it is complete - KnownElement k = stack.pop().getKnownElement(); - //if KnownElement k is closing another element, check if there is one and remove it - if (k.isClosing()) { - if (stack.empty()) { //there are no open elements at all - addError(new SyntaxError("Closed tag " + k.getShortDescription() + " without " + - "having any open tags.", -2)); - } else if (stack.peek() instanceof KnownElement) { - //get opening tag - KnownElement kFirst = (KnownElement) stack.pop(); - //check if closing element matches the opening element - if (!kFirst.isMatchingElement(k)) { - addError(new SyntaxError("Opening tag " + kFirst.getShortDescription() + - " does not match closing tag " + k.getShortDescription() + ".", -2)); - } - } else { - //There closing tag inside another not Known element: <div </html> - addError(new SyntaxError("Closing tag " + k.getShortDescription() + " inside " + - "another tag " + stack.peek().getShortDescription() + " without opening first.", - -2, true)); - } - } - //check if the element needs to be closed by another - if (k.isOpening()) { - stack.push(k); - } - return; - } - //means the element couldn't do anything with it and is finished - if (change == 4) { - stack.pop(); - } else { - return; - } - } - } - - - //innermost element was uninterested, trying to find matching element - switch (c) { - //case '@': - //stack.push(new AtElement(currentLine, currentPosition)); - //break; - case '<': - stack.push(new AngleBracketElement(currentLine, currentPosition)); - break; - //case '>': - //addError(new SyntaxError("Dangling \">\", current innermost: " + (stack.empty() ? "null" : stack.peek().getShortDescription()), -2)); - //break; - case '/': - stack.push(new CommentElement(currentLine, currentPosition)); - break; - } - } - - /** - * add an error to the error list - * - * @param e new error - */ - private static void addError(SyntaxError e) { - e.setFile(currentFile); - e.setLine(currentLine); - e.setPosition(currentPosition); - errors.add(e); - } + public static TagSearchTree<Tag> htmlTags, twineTags; + 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) { + + //setup + setupExclude(); + setupHtmlTags(); + setupTwineTags(); + Path workingDir = Paths.get("").toAbsolutePath(); + + //actual sanityCheck + runSanityCheckInDirectory(workingDir, new File("src/")); + + //output errors + for (SyntaxError e : + errors) { + System.out.println(e.getError()); + } + } + + + /** + * Goes through the whole directory including subdirectories and runs + * {@link Main#sanityCheck(Path)} on all .tw files + * + * @param dir to be checked + */ + private static void runSanityCheckInDirectory(Path workingDir, File dir) { + //subdirectories are checked recursively + + try { + for (File file : dir.listFiles()) { + if (file.isFile()) { //run sanityCheck if file is a .tw file + String path = file.getAbsolutePath(); + if (path.endsWith(".tw")) { + sanityCheck(workingDir.relativize(file.toPath())); + } + } else if (file.isDirectory()) { + runSanityCheckInDirectory(workingDir, file.getAbsoluteFile()); + } + } + } catch (NullPointerException e) { + e.printStackTrace(); + System.err.println("Couldn't read directory " + currentFile); + System.exit(-1); + } + } + + /** + * Runs the sanity check for one file. Does not run if file is excluded. + * + * @param path file to be checked + */ + 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())) { + currentFile = file.getPath(); + currentLine = 1; + stack = new Stack<>(); + + //actually opening and reading the file + try (InputStream in = new FileInputStream(file); + Reader reader = new InputStreamReader(in, encoding); + // buffer for efficiency + Reader buffer = new BufferedReader(reader)) { + handleCharacters(buffer); + } catch (IOException e) { + e.printStackTrace(); + System.err.println("Couldn't read " + file); + } + } + } + + /** + * sets up a {@link TagSearchTree<Tag>} for fast access of HTML tags later + */ + private static void setupHtmlTags() { + //load HTML tags into a list + List<Tag> TagsList = loadTags("devTools/javaSanityCheck/htmlTags"); + + //turn List into alphabetical search tree + try { + htmlTags = new TagSearchTree<>(TagsList); + } catch (ArrayIndexOutOfBoundsException e) { + System.err.println("Illegal Character in devTools/javaSanityCheck/htmlTags"); + System.exit(-1); + } + } + + /** + * sets up a {@link TagSearchTree<Tag>} for fast access of twine tags later + */ + private static void setupTwineTags() { + //load twine tags into a list + List tagsList = loadTags("devTools/javaSanityCheck/twineTags"); + + //turn List into alphabetical search tree + try { + twineTags = new TagSearchTree<>(tagsList); + } catch (ArrayIndexOutOfBoundsException e) { + System.err.println("Illegal Character in devTools/javaSanityCheck/twineTags"); + System.exit(-1); + } + } + + /** + * Loads a list of tags from a file + * + * @param filePath file to load tags from + * @return loaded tags + */ + private static List<Tag> loadTags(String filePath) { + List<Tag> tagsList = new LinkedList<>(); + try { + Files.lines(new File(filePath).toPath()).map(String::trim) + .filter(s -> !s.startsWith("#")) + .forEach(s -> tagsList.add(parseTag(s))); + } catch (IOException e) { + System.err.println("Couldn't read " + filePath); + } + return tagsList; + } + + /** + * Turns a string into a Tag + * ";1" at the end of the String indicates that the tag needs to be closed later + * + * @param s tag as String + * @return tag as Tag + */ + private static Tag parseTag(String s) { + String[] st = s.split(";"); + if (st.length > 1 && st[1].equals("1")) { + return new Tag(st[0], false); + } + return new Tag(st[0], true); + } + + /** + * sets up the excluded files array. + */ + private static void setupExclude() { + //load excluded files + 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"); + } + + //turn excluded files into an array and change path to windows style if needed + if (isWindows()) { + excluded = new String[excludedList.size()]; + int i = 0; + for (String s : + excludedList) { + excluded[i++] = s.replaceAll("/", "\\\\"); + } + } else { + excluded = excludedList.toArray(new String[0]); + } + } + + /** + * @return whether OS is Windows or not + */ + private static boolean isWindows() { + return (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows")); + } + + /** + * checks if a file or directory is excluded from the sanity check + * + * @param s file/directory to be checked + * @return whether it is excluded or not + */ + private static boolean excluded(String s) { + for (String ex : + excluded) { + if (s.startsWith(ex)) return true; + } + return false; + } + + /** + * Reads the file character by character. + * + * @param reader reader that is read + * @throws IOException thrown if the file can't be read + */ + private static void handleCharacters(Reader reader) throws IOException { + int r; + while ((r = reader.read()) != -1) { + char c = (char) r; + handleCharacter(c); + } + } + + /** + * Handles a single character + * + * @param c next character + */ + private static void handleCharacter(char c) { + //updating position + 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) { + change = e.getChange(); + addError(e); + } + + //change greater 0 means the innermost element did some work + if (change > 0) { + //2 means the Element is complete + if (change == 2) { + //remove the topmost element from stack since it is complete + stack.pop(); + return; + } + //3 means the Element is complete and part of a two or more tag system + if (change == 3) { + //remove the topmost element from stack since it is complete + KnownElement k = stack.pop().getKnownElement(); + //if KnownElement k is closing another element, check if there is one and remove it + if (k.isClosing()) { + if (stack.empty()) { //there are no open elements at all + addError(new SyntaxError("Closed tag " + k.getShortDescription() + " without " + + "having any open tags.", -2)); + } else if (stack.peek() instanceof KnownElement) { + //get opening tag + KnownElement kFirst = (KnownElement) stack.pop(); + //check if closing element matches the opening element + if (!kFirst.isMatchingElement(k)) { + addError(new SyntaxError("Opening tag " + kFirst.getShortDescription() + + " does not match closing tag " + k.getShortDescription() + ".", -2)); + } + } else { + //There closing tag inside another not Known element: <div </html> + addError(new SyntaxError("Closing tag " + k.getShortDescription() + " inside " + + "another tag " + stack.peek().getShortDescription() + " without opening first.", + -2, true)); + } + } + //check if the element needs to be closed by another + if (k.isOpening()) { + stack.push(k); + } + return; + } + //means the element couldn't do anything with it and is finished + if (change == 4) { + stack.pop(); + } else { + return; + } + } + } + + + //innermost element was uninterested, trying to find matching element + switch (c) { + //case '@': + //stack.push(new AtElement(currentLine, currentPosition)); + //break; + case '<': + stack.push(new AngleBracketElement(currentLine, currentPosition)); + break; + //case '>': + //addError(new SyntaxError("Dangling \">\", current innermost: " + (stack.empty() ? "null" : stack.peek().getShortDescription()), -2)); + //break; + case '/': + stack.push(new CommentElement(currentLine, currentPosition)); + break; + //case '(': + // stack.push(new BracketElement(currentLine, currentPosition)); + } + } + + /** + * add an error to the error list + * + * @param e new error + */ + private static void addError(SyntaxError e) { + e.setFile(currentFile); + e.setLine(currentLine); + e.setPosition(currentPosition); + errors.add(e); + } } diff --git a/devTools/javaSanityCheck/src/SyntaxError.java b/devTools/javaSanityCheck/src/SyntaxError.java index c30e5fbc101..1e1b8936f81 100644 --- a/devTools/javaSanityCheck/src/SyntaxError.java +++ b/devTools/javaSanityCheck/src/SyntaxError.java @@ -1,64 +1,67 @@ package org.arkerthan.sanityCheck; +/** + * @author Arkerthan + */ public class SyntaxError extends Exception { - private String file; - private int line, position; - private String description; - private int change; //see Element for values; -2 means not thrown - private boolean warning = false; + private String file; + private int line, position; + private String description; + private int change; //see Element for values; -2 means not thrown + private boolean warning = false; - /** - * @param description description of error - * @param change state change as specified in Element - */ - public SyntaxError(String description, int change) { - this.description = description; - this.change = change; - } + /** + * @param description description of error + * @param change state change as specified in Element + */ + public SyntaxError(String description, int change) { + this.description = description; + this.change = change; + } - /** - * @param description description of error - * @param change state change as specified in Element - * @param warning whether it is a warning or an error - */ - public SyntaxError(String description, int change, boolean warning) { - this(description, change); - this.warning = warning; - } + /** + * @param description description of error + * @param change state change as specified in Element + * @param warning whether it is a warning or an error + */ + public SyntaxError(String description, int change, boolean warning) { + this(description, change); + this.warning = warning; + } - /** - * @param file at which the error occurred - */ - public void setFile(String file) { - this.file = file; - } + /** + * @param file at which the error occurred + */ + public void setFile(String file) { + this.file = file; + } - /** - * @param line in which the error occurred - */ - public void setLine(int line) { - this.line = line; - } + /** + * @param line in which the error occurred + */ + public void setLine(int line) { + this.line = line; + } - /** - * @param position at which the error occurred - */ - public void setPosition(int position) { - this.position = position; - } + /** + * @param position at which the error occurred + */ + public void setPosition(int position) { + this.position = position; + } - /** - * @return error message - */ - public String getError() { - String s = warning ? "Warning: " : "Error: "; - return s + file + ": " + line + ":" + position + " : " + description; - } + /** + * @return error message + */ + public String getError() { + String s = warning ? "Warning: " : "Error: "; + return s + file + ": " + line + ":" + position + " : " + description; + } - /** - * @return change that happened in Element before it was thrown. -1 if not thrown. - */ - public int getChange() { - return change; - } + /** + * @return change that happened in Element before it was thrown. -1 if not thrown. + */ + public int getChange() { + return change; + } } diff --git a/devTools/javaSanityCheck/src/Tag.java b/devTools/javaSanityCheck/src/Tag.java index 5c3c92651a7..10ca580417d 100644 --- a/devTools/javaSanityCheck/src/Tag.java +++ b/devTools/javaSanityCheck/src/Tag.java @@ -1,11 +1,14 @@ package org.arkerthan.sanityCheck; +/** + * @author Arkerthan + */ public class Tag { - public final String tag; - public final boolean single; + public final String tag; + public final boolean single; - public Tag(String tag, boolean single) { - this.tag = tag; - this.single = single; - } + public Tag(String tag, boolean single) { + this.tag = tag; + this.single = single; + } } diff --git a/devTools/javaSanityCheck/src/TagSearchTree.java b/devTools/javaSanityCheck/src/TagSearchTree.java index 71e89ec769f..54c45bbd07c 100644 --- a/devTools/javaSanityCheck/src/TagSearchTree.java +++ b/devTools/javaSanityCheck/src/TagSearchTree.java @@ -3,78 +3,79 @@ package org.arkerthan.sanityCheck; import java.util.List; /** + * @param <E> Tag class to be stored + * @author Arkerthan + * <p> * Tag SearchTree stores Tags in an alphabetical search tree. * Once created the search tree can't be changed anymore. - * - * @param <E> Tag class to be stored */ public class TagSearchTree<E extends Tag> { - private static final int SIZE = 128; - private final TagSearchTree<E>[] branches; - private E element = null; - private String path; + private static final int SIZE = 128; + private final TagSearchTree<E>[] branches; + private E element = null; + private String path; - /** - * creates a new empty TagSearchTree - */ - private TagSearchTree() { - branches = new TagSearchTree[SIZE]; - } + /** + * creates a new empty TagSearchTree + */ + private TagSearchTree() { + branches = new TagSearchTree[SIZE]; + } - /** - * Creates a new filled TagSearchTree - * - * @param list Tags to be inserted - */ - public TagSearchTree(List<E> list) { - this(); - for (E e : list) { - this.add(e, 0); - } - } + /** + * Creates a new filled TagSearchTree + * + * @param list Tags to be inserted + */ + public TagSearchTree(List<E> list) { + this(); + for (E e : list) { + this.add(e, 0); + } + } - /** - * adds a new Tag to the TagSearchTree - * - * @param e Tag to be stored - * @param index index of relevant char for adding in tag - */ - private void add(E e, int index) { - //set the path to here - path = e.tag.substring(0, index); - //checks if tag has to be stored here or further down - if (e.tag.length() == index) { - element = e; - } else { - //store tag in correct branch - char c = e.tag.charAt(index); - if (branches[c] == null) { - branches[c] = new TagSearchTree<>(); - } - branches[c].add(e, index + 1); - } - } + /** + * adds a new Tag to the TagSearchTree + * + * @param e Tag to be stored + * @param index index of relevant char for adding in tag + */ + private void add(E e, int index) { + //set the path to here + path = e.tag.substring(0, index); + //checks if tag has to be stored here or further down + if (e.tag.length() == index) { + element = e; + } else { + //store tag in correct branch + char c = e.tag.charAt(index); + if (branches[c] == null) { + branches[c] = new TagSearchTree<>(); + } + branches[c].add(e, index + 1); + } + } - /** - * @param c character of branch needed - * @return branch or null if branch doesn't exist - */ - public TagSearchTree<E> getBranch(char c) { - if (c >= SIZE) return null; - return branches[c]; - } + /** + * @param c character of branch needed + * @return branch or null if branch doesn't exist + */ + public TagSearchTree<E> getBranch(char c) { + if (c >= SIZE) return null; + return branches[c]; + } - /** - * @return stored Tag, null if empty - */ - public E getElement() { - return element; - } + /** + * @return stored Tag, null if empty + */ + public E getElement() { + return element; + } - /** - * @return path inside full tree to get to this Branch - */ - public String getPath() { - return path; - } + /** + * @return path inside full tree to get to this Branch + */ + public String getPath() { + return path; + } } diff --git a/devTools/javaSanityCheck/src/UnknownStateException.java b/devTools/javaSanityCheck/src/UnknownStateException.java index 80dbe598682..77c91e73e10 100644 --- a/devTools/javaSanityCheck/src/UnknownStateException.java +++ b/devTools/javaSanityCheck/src/UnknownStateException.java @@ -1,8 +1,11 @@ package org.arkerthan.sanityCheck; +/** + * @author Arkerthan + */ public class UnknownStateException extends RuntimeException { - public UnknownStateException(int state) { - super(String.valueOf(state)); - } + 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 4b3f2526a39..8db115bf04c 100644 --- a/devTools/javaSanityCheck/src/element/AngleBracketElement.java +++ b/devTools/javaSanityCheck/src/element/AngleBracketElement.java @@ -5,6 +5,9 @@ import org.arkerthan.sanityCheck.*; import java.util.Arrays; import java.util.List; +/** + * @author Arkerthan + */ public class AngleBracketElement extends Element { private static final List<String> logicTags = Arrays.asList("if", "elseif", "else", "switch", "case", "default"); private int state = 0; @@ -309,7 +312,7 @@ public class AngleBracketElement extends Element { @Override public String getShortDescription() { StringBuilder builder = new StringBuilder(); - builder.append('[').append(line).append(":").append(pos).append("] "); + builder.append(getPositionAsString()).append(" "); switch (state) { case 0: builder.append("<"); diff --git a/devTools/javaSanityCheck/src/element/AtElement.java b/devTools/javaSanityCheck/src/element/AtElement.java index 168e340a432..598dad63eea 100644 --- a/devTools/javaSanityCheck/src/element/AtElement.java +++ b/devTools/javaSanityCheck/src/element/AtElement.java @@ -3,103 +3,106 @@ package org.arkerthan.sanityCheck.element; import org.arkerthan.sanityCheck.SyntaxError; import org.arkerthan.sanityCheck.UnknownStateException; +/** + * @author Arkerthan + */ public class AtElement extends Element { - private int state = 0; - // 0 = @ - // 1 = @@ - // 2 = @@. - // 3 = @@.a -- @@.ab -- @@.abc - // 4 = @@.abc;abc - // 5 = @@.abc;abc@ + private int state = 0; + // 0 = @ + // 1 = @@ + // 2 = @@. + // 3 = @@.a -- @@.ab -- @@.abc + // 4 = @@.abc;abc + // 5 = @@.abc;abc@ - // example: @@.red;some text@@ + // example: @@.red;some text@@ - public AtElement(int line, int pos) { - super(line, pos); - } + public AtElement(int line, int pos) { + super(line, pos); + } - @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 UnknownStateException(state); - } - return 0; - } + @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 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(); - } + @Override + public String getShortDescription() { + StringBuilder builder = new StringBuilder(); + builder.append(getPositionAsString()).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/BracketElement.java b/devTools/javaSanityCheck/src/element/BracketElement.java new file mode 100644 index 00000000000..b9848941653 --- /dev/null +++ b/devTools/javaSanityCheck/src/element/BracketElement.java @@ -0,0 +1,28 @@ +package org.arkerthan.sanityCheck.element; + +import org.arkerthan.sanityCheck.SyntaxError; + +/** + * @author Arkerthan + */ +public class BracketElement extends Element { + //int state = 0; + + public BracketElement(int line, int pos) { + super(line, pos); + } + + @Override + public int handleChar(char c) throws SyntaxError { + if (c == ')') { + return 2; + } else { + return 0; + } + } + + @Override + public String getShortDescription() { + return getPositionAsString() + " (???"; + } +} diff --git a/devTools/javaSanityCheck/src/element/CommentElement.java b/devTools/javaSanityCheck/src/element/CommentElement.java index a1c80c70042..a03c929a211 100644 --- a/devTools/javaSanityCheck/src/element/CommentElement.java +++ b/devTools/javaSanityCheck/src/element/CommentElement.java @@ -3,6 +3,9 @@ package org.arkerthan.sanityCheck.element; import org.arkerthan.sanityCheck.SyntaxError; import org.arkerthan.sanityCheck.UnknownStateException; +/** + * @author Arkerthan + */ public class CommentElement extends Element { private int state = 0; /* @@ -43,7 +46,7 @@ public class CommentElement extends Element { case 2: if (c == '/') { return 2; - } else if (c == '*'){ + } else if (c == '*') { return 1; } state = 1; @@ -67,6 +70,6 @@ public class CommentElement extends Element { @Override public String getShortDescription() { - return null; + return getPositionAsString() + "comment"; } } diff --git a/devTools/javaSanityCheck/src/element/Element.java b/devTools/javaSanityCheck/src/element/Element.java index d0e1bf7cd12..02400f2a531 100644 --- a/devTools/javaSanityCheck/src/element/Element.java +++ b/devTools/javaSanityCheck/src/element/Element.java @@ -2,42 +2,54 @@ package org.arkerthan.sanityCheck.element; import org.arkerthan.sanityCheck.SyntaxError; +/** + * @author Arkerthan + */ public abstract class Element { - protected KnownElement k; - protected int line, pos; + protected KnownElement 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; - } + /** + * @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 - * 4 - the Element is finished and the char is still open for use - * - * @param c char to be parsed - * @return state change - * @throws SyntaxError thrown when an syntax error is detected - */ - public abstract int handleChar(char c) throws SyntaxError; + /** + * 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 + * 4 - the Element is finished and the char is still open for use + * + * @param c char to be parsed + * @return state change + * @throws SyntaxError thrown when an syntax error is detected + */ + public abstract int handleChar(char c) throws SyntaxError; - /** - * @return the constructed KnownElement. null if none was constructed yet. - */ - public KnownElement getKnownElement() { - return k; - } + /** + * @return the constructed KnownElement. null if none was constructed yet. + */ + public KnownElement getKnownElement() { + return k; + } - /** - * @return a short description usually based on state and position of the Element - */ - public abstract String getShortDescription(); + /** + * Returns the line and position of the Element in the file it was created in. + * + * @return position of Element in file as String + */ + public String getPositionAsString() { + return "[" + line + ":" + pos + "]"; + } + + /** + * @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/KnownElement.java index d09a5a7a5cc..9b64eb18a87 100644 --- a/devTools/javaSanityCheck/src/element/KnownElement.java +++ b/devTools/javaSanityCheck/src/element/KnownElement.java @@ -2,34 +2,37 @@ package org.arkerthan.sanityCheck.element; import org.arkerthan.sanityCheck.SyntaxError; +/** + * @author Arkerthan + */ public abstract class KnownElement extends Element { - /** - * @param line at which it begins - * @param pos at which it begins - */ - public KnownElement(int line, int pos) { - super(line, pos); - } + /** + * @param line at which it begins + * @param pos at which it begins + */ + public KnownElement(int line, int pos) { + super(line, pos); + } - /** - * @return true, if it needs another Known Element to close it. - */ - public abstract boolean isOpening(); + /** + * @return true, if it needs another Known Element to close it. + */ + public abstract boolean isOpening(); - /** - * @return true if it closes another Element. - */ - public abstract boolean isClosing(); + /** + * @return true if it closes another Element. + */ + public abstract boolean isClosing(); - /** - * @param k Element to be checked - * @return true if given Element closes Element - */ - public abstract boolean isMatchingElement(KnownElement k); + /** + * @param k Element to be checked + * @return true if given Element closes Element + */ + public abstract boolean isMatchingElement(KnownElement k); - @Override - public int handleChar(char c) throws SyntaxError { - return 0; - } + @Override + public int handleChar(char c) throws SyntaxError { + return 0; + } } diff --git a/devTools/javaSanityCheck/src/element/KnownHtmlElement.java b/devTools/javaSanityCheck/src/element/KnownHtmlElement.java index 8ae21e1a402..6edaa6723ba 100644 --- a/devTools/javaSanityCheck/src/element/KnownHtmlElement.java +++ b/devTools/javaSanityCheck/src/element/KnownHtmlElement.java @@ -1,47 +1,50 @@ package org.arkerthan.sanityCheck.element; +/** + * @author Arkerthan + */ public class KnownHtmlElement extends KnownElement { - private boolean opening; - private String statement; + private boolean opening; + private String statement; - /** - * @param line at which it begins - * @param pos at which it begins - * @param opening if it opens a tag: <tag> or closes it: </tag> - * @param statement statement inside the tag - */ - public KnownHtmlElement(int line, int pos, boolean opening, String statement) { - super(line, pos); - this.opening = opening; - this.statement = statement; - } + /** + * @param line at which it begins + * @param pos at which it begins + * @param opening if it opens a tag: <tag> or closes it: </tag> + * @param statement statement inside the tag + */ + public KnownHtmlElement(int line, int pos, boolean opening, String statement) { + super(line, pos); + this.opening = opening; + this.statement = statement; + } - @Override - public String getShortDescription() { - StringBuilder builder = new StringBuilder(); - builder.append('[').append(line).append(":").append(pos).append("] <"); - if (!opening) { - builder.append("/"); - } - return builder.append(statement).append(">").toString(); - } + @Override + public String getShortDescription() { + StringBuilder builder = new StringBuilder(); + builder.append(getPositionAsString()).append(" <"); + if (!opening) { + builder.append("/"); + } + return builder.append(statement).append(">").toString(); + } - @Override - public boolean isOpening() { - return opening; - } + @Override + public boolean isOpening() { + return opening; + } - @Override - public boolean isClosing() { - return !opening; - } + @Override + public boolean isClosing() { + return !opening; + } - @Override - public boolean isMatchingElement(KnownElement k) { - if (k instanceof KnownHtmlElement) { - return ((KnownHtmlElement) k).statement.equals(this.statement); - } - return false; - } + @Override + public boolean isMatchingElement(KnownElement k) { + if (k instanceof KnownHtmlElement) { + return ((KnownHtmlElement) k).statement.equals(this.statement); + } + return false; + } } diff --git a/devTools/javaSanityCheck/src/element/KnownLogicElement.java b/devTools/javaSanityCheck/src/element/KnownLogicElement.java index 994201c5a9b..e887b298936 100644 --- a/devTools/javaSanityCheck/src/element/KnownLogicElement.java +++ b/devTools/javaSanityCheck/src/element/KnownLogicElement.java @@ -6,112 +6,115 @@ import org.arkerthan.sanityCheck.UnknownStateException; import java.util.Arrays; import java.util.List; +/** + * @author Arkerthan + */ public class KnownLogicElement extends KnownElement { - private static final List<String> allowedTags = Arrays.asList("if", "elseif", "else"); - private final int state; - private boolean last; - /* - 0 - if - 1 - elseif - 2 - else - 3 - switch - 4 - case - 5 - default - */ + private static final List<String> allowedTags = Arrays.asList("if", "elseif", "else"); + private final int state; + private boolean last; + /* + 0 - if + 1 - elseif + 2 - else + 3 - switch + 4 - case + 5 - default + */ - public KnownLogicElement(int line, int pos, String tag, boolean last) { - this(line, pos, tag); - this.last = last; - } + public KnownLogicElement(int line, int pos, String tag, boolean last) { + this(line, pos, tag); + this.last = last; + } - public KnownLogicElement(int line, int pos, String tag) { - super(line, pos); - switch (tag) { - case "if": - state = 0; - break; - case "elseif": - state = 1; - break; - case "else": - state = 2; - break; - case "switch": - state = 3; - break; - case "case": - state = 4; - break; - case "default": - state = 5; - break; - default: - throw new DisallowedTagException(tag); - } - last = false; - } + public KnownLogicElement(int line, int pos, String tag) { + super(line, pos); + switch (tag) { + case "if": + state = 0; + break; + case "elseif": + state = 1; + break; + case "else": + state = 2; + break; + case "switch": + state = 3; + break; + case "case": + state = 4; + break; + case "default": + state = 5; + break; + default: + throw new DisallowedTagException(tag); + } + last = false; + } - @Override - public boolean isOpening() { - return !last; - } + @Override + public boolean isOpening() { + return !last; + } - @Override - public boolean isClosing() { - return (state != 0 && state != 3) || last; - } + @Override + public boolean isClosing() { + return (state != 0 && state != 3) || last; + } - @Override - public boolean isMatchingElement(KnownElement k) { - if (!(k instanceof KnownLogicElement)) { - return false; - } - KnownLogicElement l = (KnownLogicElement) k; - switch (state) { - case 0: - case 1: - return l.state == 1 || l.state == 2 || (l.state == 0 && l.last); - case 2: - return l.state == 0 && l.last; - case 3: - case 4: - return l.state == 3 || l.state == 4; - case 5: - return l.state == 3 && l.last; - default: - throw new UnknownStateException(state); - } - } + @Override + public boolean isMatchingElement(KnownElement k) { + if (!(k instanceof KnownLogicElement)) { + return false; + } + KnownLogicElement l = (KnownLogicElement) k; + switch (state) { + case 0: + case 1: + return l.state == 1 || l.state == 2 || (l.state == 0 && l.last); + case 2: + return l.state == 0 && l.last; + case 3: + case 4: + return l.state == 3 || l.state == 4; + case 5: + return l.state == 3 && l.last; + default: + throw new UnknownStateException(state); + } + } - @Override - public String getShortDescription() { - StringBuilder builder = new StringBuilder(); - builder.append("[").append(line).append(":").append(pos).append("] <<"); - if (last) { - builder.append('/'); - } - switch (state) { - case 0: - builder.append("if"); - break; - case 1: - builder.append("elseif"); - break; - case 2: - builder.append("else"); - break; - case 3: - builder.append("switch"); - break; - case 4: - builder.append("case"); - break; - case 5: - builder.append("default"); - break; - default: - throw new UnknownStateException(state); - } - return builder.append(">>").toString(); - } + @Override + public String getShortDescription() { + StringBuilder builder = new StringBuilder(); + builder.append(getPositionAsString()).append(" <<"); + if (last) { + builder.append('/'); + } + switch (state) { + case 0: + builder.append("if"); + break; + case 1: + builder.append("elseif"); + break; + case 2: + builder.append("else"); + break; + case 3: + builder.append("switch"); + break; + case 4: + builder.append("case"); + break; + case 5: + builder.append("default"); + break; + default: + throw new UnknownStateException(state); + } + return builder.append(">>").toString(); + } } diff --git a/devTools/javaSanityCheck/src/element/KnownTwineElement.java b/devTools/javaSanityCheck/src/element/KnownTwineElement.java index 4bd78b2d585..020aad7819f 100644 --- a/devTools/javaSanityCheck/src/element/KnownTwineElement.java +++ b/devTools/javaSanityCheck/src/element/KnownTwineElement.java @@ -1,48 +1,50 @@ package org.arkerthan.sanityCheck.element; +/** + * @author Arkerthan + */ public class KnownTwineElement extends KnownElement { - private boolean opening; - private String statement; + private boolean opening; + private String statement; - /** - * - * @param line at which it begins - * @param pos at which it begins - * @param opening if it opens a tag: <<tag>> or closes it: <</tag>> - * @param statement statement inside the tag - */ - public KnownTwineElement(int line, int pos, boolean opening, String statement) { - super(line, pos); - this.opening = opening; - this.statement = statement; - } + /** + * @param line at which it begins + * @param pos at which it begins + * @param opening if it opens a tag: <<tag>> or closes it: <</tag>> + * @param statement statement inside the tag + */ + public KnownTwineElement(int line, int pos, boolean opening, String statement) { + super(line, pos); + this.opening = opening; + this.statement = statement; + } - @Override - public String getShortDescription() { - StringBuilder builder = new StringBuilder(); - builder.append("[").append(line).append(":").append(pos).append("] <<"); - if (!opening) { - builder.append("/"); - } - return builder.append(statement).append(">>").toString(); - } + @Override + public String getShortDescription() { + StringBuilder builder = new StringBuilder(); + builder.append(getPositionAsString()).append(" <<"); + if (!opening) { + builder.append("/"); + } + return builder.append(statement).append(">>").toString(); + } - @Override - public boolean isOpening() { - return opening; - } + @Override + public boolean isOpening() { + return opening; + } - @Override - public boolean isClosing() { - return !opening; - } + @Override + public boolean isClosing() { + return !opening; + } - @Override - public boolean isMatchingElement(KnownElement k) { - if (k instanceof KnownTwineElement) { - return ((KnownTwineElement) k).statement.equals(this.statement); - } - return false; - } + @Override + public boolean isMatchingElement(KnownElement k) { + if (k instanceof KnownTwineElement) { + return ((KnownTwineElement) k).statement.equals(this.statement); + } + return false; + } } -- GitLab