<|start_filename|>src/io/github/keheck/tree/NodeIconProvider.java<|end_filename|>
package io.github.keheck.tree;

import org.jdesktop.swingx.renderer.IconValue;

import javax.swing.*;

public class NodeIconProvider implements IconValue
{
    @Override
    public Icon getIcon(Object value)
    {
        if(value instanceof AbstractNavTreeNode)
        {
            return ((AbstractNavTreeNode)value).getType().getIcon();
        }

        return null;
    }
}


<|start_filename|>src/io/github/keheck/project/compiling/Compiler.java<|end_filename|>
package io.github.keheck.project.compiling;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.keheck.Main;
import io.github.keheck.Tasks;
import io.github.keheck.project.exceptions.*;
import io.github.keheck.project.saveandload.Save;
import io.github.keheck.tree.*;
import io.github.keheck.util.Log;
import io.github.keheck.window.MainMenu;
import io.github.keheck.window.dialogs.DialogCompilationFail;
import io.github.keheck.window.dialogs.DialogCompilationSuccess;

import java.io.File;
import java.util.*;

import static io.github.keheck.project.compiling.PreCompiler.char1CompArray;
import static io.github.keheck.project.compiling.PreCompiler.char2CompArray;

public class Compiler
{
    static HashMap<NavTreeFile, ArrayList<String>> mcfunctionCode = new HashMap<>();
    static HashMap<NavTreeFile, ArrayList<String>> compiledVirtualCode = new HashMap<>();
    static HashMap<NavTreeFile, ArrayList<NavTreeFolder>> filePaths = new HashMap<>();
    private static HashMap<VirtualIfNode, ArrayList<String>> virtualIfs = new HashMap<>();
    private static HashMap<VirtualUnlessNode, ArrayList<String>> virtualUnlesss = new HashMap<>();
    static ArrayList<ArrayList<NavTreeFolder>> presentPaths = new ArrayList<>();
    static ArrayList<NavTreeFile> skippedFiles = new ArrayList<>();
    static HashMap<String, File> nodePathMap = new HashMap<>();
    static File root;
    static NavTreeFolder rootNode;

    static ArrayList<String> errors = new ArrayList<>();
    static ArrayList<String> warnings = new ArrayList<>();

    static boolean failed = false;

    static Tasks.DialogUpdater thread;

    public static void init(File path)
    {
        thread = (Tasks.DialogUpdater)Tasks.constructDynamicDialog();
        thread.start();

        Log.f1("Initializing Compiler...");
        Save.cleanDirtyFile();
        root = new File(path, Main.project);

        if(root.exists())
        {
            Log.f2("Found an old root node! Deleting to make space...");
            deleteChildren(root);
        }

        root.mkdirs();
        rootNode = (NavTreeFolder)Main.navTree.getModel().getRoot();

        Set<NavTreeFile> keySet = Main.texts.keySet();

        for(NavTreeFile key : keySet)
        {
            ArrayList<NavTreeFolder> fromRootToNode = new ArrayList<>();
            NavTreeFolder parent = (NavTreeFolder)key.getParent();

            while(parent != null && parent != rootNode)
            {
                fromRootToNode.add(parent);
                parent = (NavTreeFolder)parent.getParent();
            }

            Collections.reverse(fromRootToNode);
            filePaths.put(key, fromRootToNode);

            if(key.getType() == AbstractNavTreeNode.NodeType.MCFUNCTION)
                mcfunctionCode.put(key, Main.texts.get(key));
        }

        Log.f1("Compiler initialized!");
        Log.f1("Flushing writing all .json files");
        PostCompiler.writeMCMeta();
        PostCompiler.writeJsons();
        if(MainMenu.commandsOnly.getState()) { PostCompiler.writeMCFunctions(); }
        else
        {
            try
            {
                preCompile();
                compile();
                compileVirtuals();
            }
            catch(CompilationException e)
            {
                if(e.isError())
                    errors.add(e.getMessage());
                else
                    warnings.add(e.getMessage());
            }
        }

        Log.i("Warnings encountered: " + warnings.size());
        Log.i("Errors encountered: " + errors.size());

        if(warnings.size() != 0)
            for(String warning : warnings)
                Log.w(warning);

        if(errors.size() != 0)
            for(String error : errors)
                Log.e(error);

        thread.interrupt();

        if(failed)
        {
            Log.e("Compilation failed!");
            new DialogCompilationFail();
        }
        else
        {
            PostCompiler.writeMCFunctions();
            PostCompiler.writeVirtuals();
            Log.i("Compilation finished! Project saved at: " + root.getAbsolutePath());
            new DialogCompilationSuccess(root.getAbsolutePath());
        }

        deinit();
    }

    /**
     * Here is all the stuff handled that doesn't (really) contribute to
     * converting the source code into the command blocks
     */
    private static void preCompile()
    {
        Log.f1("Starting pre-compilation process...");

        Set<NavTreeFile> keys = mcfunctionCode.keySet();

        for(NavTreeFile key : keys)
        {
            thread.updateDialog("Simplifying " + key.getPath());
            mcfunctionCode.put(key, PreCompiler.preCompile(key, mcfunctionCode.get(key)));
        }
    }

    private static void compile()
    {
        Log.f1("Starting true compilation...");

        Set<NavTreeFile> keys = mcfunctionCode.keySet();

        for(NavTreeFile key : keys)
        {
            thread.updateDialog("Compiling " + key.getPath());
            mcfunctionCode.put(key, compileFile(key));
        }
    }

    private static void compileVirtuals()
    {
        Set<VirtualIfNode> ifKeys = virtualIfs.keySet();
        Set<VirtualUnlessNode> unlessKeys = virtualUnlesss.keySet();

        for (VirtualIfNode node : ifKeys)
        {
            thread.updateDialog("Compiling if " + node.getPath());
            compiledVirtualCode.put(node, compileFile(node));
            virtualIfs.remove(node);
        }

        for(VirtualUnlessNode node : unlessKeys)
        {
            thread.updateDialog("Compiling unless " + node.getPath());
            compiledVirtualCode.put(node, compileFile(node));
            virtualUnlesss.remove(node);
        }

        if(virtualUnlesss.size() != 0 || virtualIfs.size() != 0)
            compileVirtuals();
    }

    private static ArrayList<String> compileFile(NavTreeFile key)
    {
        ArrayList<String> text;

        if(key instanceof VirtualIfNode)
            text = virtualIfs.get(key);
        else if(key instanceof VirtualUnlessNode)
            text = virtualUnlesss.get(key);
        else
            text = mcfunctionCode.get(key);

        ArrayList<String> compiled = new ArrayList<>();
        //variable name, coordinates
        HashMap<String, NumberGroup> blocks = new HashMap<>();

        {
            int codeBlocks = 0;

            for(String s : text)
                if(s.equals("{")) codeBlocks++;
                else if(s.equals("}")) codeBlocks--;

            if(codeBlocks < 0)
                throw new SyntaxException("imbalance of \"{\" and \"}\".", 0, key.getPath());
        }

        for(int i = 0; i < text.size(); i++)
        {
            try
            {
                String line = text.get(i);

                if(line.equals("{") || line.equals("}")) continue;

                if(line.startsWith("run"))
                {
                    compiled.add(line.substring(4));
                }
                else if(line.startsWith("var"))
                {
                    line = line.substring(4);

                    if(line.matches("[a-zA-Z0-9]+=-?\\d+"))
                    {
                        String[] parts = line.split("=");
                        String command = "scoreboard players set " + parts[0] + " vars " + parts[1];
                        compiled.add(command);
                    }
                    else if(line.matches("[a-zA-Z0-9]+(\\+=|-=|\\*=|/=|%=|<|>|><|=)[a-zA-Z0-9]+"))
                    {
                        String[] parts = line.split("(\\+=|-=|\\*=|/=|%=|<|>|><|=)");
                        String operand = line.substring(parts[0].length(), parts[0].length()+2);
                        if(operand.matches("[><=].")) operand = operand.substring(0, 1);
                        String command = "scoreboard players operation " + parts[0] + " vars " + operand + " " + parts[1] + " vars";
                        compiled.add(command);
                    }
                    else
                    {
                        throw new UnknownStatementException("Not a known syntax for command \"var\": " + line, i+1, key.getPath());
                    }
                }
                else if(line.startsWith("fnc"))
                {
                    line = line.substring(4);

                    if(line.matches("[a-z0-9_]+->[a-z0-9_]+"))
                    {
                        String[] parts = line.split("->");
                        String command = "function " + parts[0] + ":" + parts[1];
                        compiled.add(command);
                    }
                    else if(line.matches("[a-z0-9_]+\\?[a-z0-9_]+"))
                    {
                        String[] parts = line.split("\\?");
                        String command = "function #" + parts[0] + ":" + parts[1];
                        compiled.add(command);
                    }
                    else
                    {
                        throw new UnknownStatementException("Not a known syntax for command \"fnc\": " + line, i+1, key.getPath());
                    }
                }
                else if(line.startsWith("blk"))
                {
                    line = line.substring(4);

                    if(line.matches("[a-zA-Z0-9]+=-?\\d+,-?\\d+,-?\\d+"))
                    {
                        String[] parts = line.split("=");
                        String[] nums = parts[1].split(",");
                        blocks.put(parts[0], new NumberGroup
                                (Integer.parseInt(nums[0]), Integer.parseInt(nums[1]), Integer.parseInt(nums[2])));
                    }
                    else if(line.matches("[a-zA-Z0-9]+\\.type=§[a-z_]+§(\\[([a-z_0-9]+=[a-zA-Z0-9_]+,)*[a-z_0-9]+=[a-zA-Z0-9_]+])?(\\{[\\s\\S]+})?"))
                    {
                        String block = line.substring(0, line.indexOf('.'));
                        String leftPart = line.substring(line.indexOf("=")+1, line.length());
                        String type = leftPart.substring(1, leftPart.indexOf('§', 2));
                        String nbt = "";
                        String state = "";
                        if(leftPart.indexOf('{') > -1) nbt = leftPart.substring(leftPart.indexOf('{'));
                        if(leftPart.indexOf('[') > -1 && leftPart.indexOf('[') < leftPart.indexOf('{')) state = leftPart.substring(leftPart.indexOf('['), leftPart.indexOf(']') + 1);

                        if(!leftPart.equals(""))
                            try { new ObjectMapper().readTree(nbt);}
                            catch (JsonProcessingException e) { throw new WrappedCompilationException(i+1, e, key.getPath()); }
                        if(!blocks.containsKey(block)) throw new UnexpectedTokenException("block variable " + block + " does not exist!", i+1, key.getPath());

                        NumberGroup coords = blocks.get(block);
                        String command = "setblock " + coords.get(0) + " " + coords.get(1) + " " + coords.get(2) + " " + type + state + nbt;
                        compiled.add(command);
                    }
                    else if(line.matches("[a-zA-Z0-9]+(\\.[a-zA-Z]+)+->((-?(\\d*?\\.\\d+)|-?(\\d+)[sfdbL]?)|(\"[\\s\\S]*\")|\\{[\\s\\S]+})"))
                    {
                        String block = line.split("->")[0].substring(0, line.indexOf('.'));
                        String path = line.substring(block.length()+1, line.indexOf("->"));
                        String value = line.substring(line.indexOf("->")+2);

                        if(value.startsWith("{"))
                            try { new ObjectMapper().readTree(value); }
                            catch (JsonProcessingException e) { throw new WrappedCompilationException(i+1, e, key.getPath()); }
                        if(!blocks.containsKey(block)) throw new UnexpectedTokenException("Could not find block variable " + block, i+1, key.getPath());

                        int[] coords = blocks.get(block).getGroup();
                        String command = "data modify block " + coords[0] + " " + coords[1] + " " + coords[2] + " " + path + " append value " + value;
                        compiled.add(command);
                    }
                    else if(line.matches("[a-zA-Z0-9]+(\\.[a-zA-Z]+)+<-((-?(\\d*?\\.\\d+)|-?(\\d+)[sfdbL]?)|(\"[\\s\\S]*\")|\\{[\\s\\S]+})"))
                    {
                        String block = line.split("<-")[0].substring(0, line.indexOf('.'));
                        String path = line.substring(block.length()+1, line.indexOf("<-"));
                        String value = line.substring(line.indexOf("<-")+2);

                        if(value.startsWith("{"))
                            try { new ObjectMapper().readTree(value); }
                            catch (JsonProcessingException e) { throw new WrappedCompilationException(i+1, e, key.getPath()); }
                        if(!blocks.containsKey(block)) throw new UnexpectedTokenException("Could not find block variable " + block, i+1, key.getPath());

                        int[] coords = blocks.get(block).getGroup();
                        String command = "data modify block " + coords[0] + " " + coords[1] + " " + coords[2] + " " + path + " prepend value " + value;
                        compiled.add(command);
                    }
                    else if(line.matches("[a-zA-Z0-9]+(\\.[a-zA-Z]+)+\\[\\d+]\\+=((-?(\\d*?\\.\\d+)|-?(\\d+)[sfdbL]?)|(\"[\\s\\S]*\")|\\{[\\s\\S]+})"))
                    {
                        String block = line.split("=")[0].substring(0, line.indexOf('.'));
                        String path = line.substring(block.length()+1, line.indexOf("["));
                        String value = line.substring(line.indexOf("+=")+2);
                        String index = line.split("\\[")[1];
                        index = index.substring(0, index.indexOf(']'));

                        if(value.startsWith("{"))
                            try { new ObjectMapper().readTree(value); }
                            catch (JsonProcessingException e) { throw new WrappedCompilationException(i+1, e, key.getPath()); }
                        if(!blocks.containsKey(block)) throw new UnexpectedTokenException("Could not find block variable " + block, i+1, key.getPath());

                        int[] coords = blocks.get(block).getGroup();
                        String command = "data modify block " + coords[0] + " " + coords[1] + " " + coords[2] + " " + path + " insert " + index +  " value " + value;
                        compiled.add(command);
                    }
                    else
                    {
                        throw new UnknownStatementException("Not a known syntax for command \"blk\": " + line, i+1, key.getPath());
                    }
                }
                else if(line.startsWith("if"))
                {
                    String condition = line.substring(line.indexOf("(")+1, line.indexOf(")"));

                    if(!text.get(i+1).equals("{"))
                        throw new ExpectedTokenException("\"{\" after if/unless statement", i+1, key.getPath());

                    ArrayList<String> ifCode = new ArrayList<>();
                    int codeBlocks = 1;
                    int j;

                    for(j = i+2; j < text.size() && codeBlocks > 0; j++)
                    {
                        String ifLine = text.get(j);
                        if(ifLine.equals("{")) codeBlocks++;
                        else if(ifLine.equals("}")) codeBlocks--;
                        ifCode.add(ifLine);
                    }

                    ifCode.remove(ifCode.size()-1);
                    i = j-1;
                    VirtualIfNode node = new VirtualIfNode();
                    int compIndex = -1;
                    String comparator = "=";
                    String command = "execute if ";

                    for(String char2Comp : char2CompArray)
                    {
                        compIndex = condition.indexOf(char2Comp);
                        if(compIndex != -1)
                        {
                            comparator = char2Comp;
                            break;
                        }
                    }

                    if(compIndex == -1)
                    {
                        for(String char1Comp : char1CompArray)
                        {
                            compIndex = condition.indexOf(char1Comp);
                            if(compIndex != -1)
                            {
                                comparator = char1Comp;
                                break;
                            }
                        }
                    }

                    if(compIndex == -1) throw new ExpectedTokenException("expected \"=\", \">=\", \"<=\", \"<\" or \">\"", i+1, key.getPath());

                    if(condition.matches("[a-zA-Z0-9]+" + comparator + "[a-zA-Z0-9]+"))
                    {
                        String[] args = condition.split(comparator);
                        boolean secArgIsNum;
                        try { Integer.parseInt(args[1]); secArgIsNum = true;}
                        catch(NumberFormatException ignored) { secArgIsNum = false; }
                        //ex: execute if score foo vars = bar vars run function helloworldifs:if0
                        if(secArgIsNum)
                            command += "score " + args[0] + " vars matches " + args[1] + " run function " + Main.project.toLowerCase() + "ifs:" + node.getName();
                        else
                            command += "score " + args[0] + " vars " + comparator + " " + args[1] + " vars" + " run function " + Main.project.toLowerCase() + "ifs:" + node.getName();
                    }
                    else if(condition.matches("[a-zA-Z0-9]+=§[a-z_]+§(\\[([a-zA-Z0-9]+=[a-zA-Z0-9]+,)*[a-zA-Z0-9]+=[a-zA-Z0-9]+])?(\\{[\\s\\S]+})?"))
                    {
                        String[] sides = new String[]{condition.substring(0, condition.indexOf('=')), condition.substring(condition.indexOf('=')+1)};
                        String block = sides[0];
                        if(!blocks.containsKey(block)) throw new UnexpectedTokenException("block variable " + block + " does not exist!", i+1, key.getPath());
                        NumberGroup group = blocks.get(block);
                        String type = sides[1].substring(1, condition.indexOf('§', 1));
                        int nbt = condition.indexOf('{');
                        int state = condition.indexOf('[');
                        String jsonText = "";
                        String stateText = "";

                        if(nbt != -1)
                        {
                            jsonText = condition.substring(nbt, condition.length());
                            try { new ObjectMapper().readTree(jsonText); }
                            catch (JsonProcessingException e) { throw new WrappedCompilationException(i+1, e, key.getPath()); }
                        }

                        if(state != -1 && state < nbt)
                        {
                            stateText = condition.substring(state, condition.indexOf(']')+1);
                        }

                        command += group.get(0) + " " + group.get(1) + " " + group.get(2) + " " + type + jsonText + stateText
                                + " run function " + Main.project.toLowerCase() + "ifs:" + node.getName();
                    }
                    else
                    {
                        throw new UnknownStatementException("Not a known syntax for command \"if\": " + line, i+1, key.getPath());
                    }

                    virtualIfs.put(node, ifCode);
                    compiled.add(command);


                }
                else if(line.startsWith("unless"))
                {
                    String condition = line.substring(line.indexOf("(")+1, line.indexOf(")"));

                    if(!text.get(i+1).equals("{"))
                        throw new ExpectedTokenException("\"{\" after if/unless statement", i+1, key.getPath());

                    ArrayList<String> ifCode = new ArrayList<>();
                    int codeBlocks = 1;
                    int j;

                    for(j = i+2; j < text.size() && codeBlocks > 0; j++)
                    {
                        String ifLine = text.get(j);
                        if(ifLine.equals("{")) codeBlocks++;
                        else if(ifLine.equals("}")) codeBlocks--;
                        ifCode.add(ifLine);
                    }

                    ifCode.remove(ifCode.size()-1);
                    VirtualUnlessNode node = new VirtualUnlessNode();
                    int compIndex = -1;
                    String comparator = "=";
                    String command = "execute unless ";

                    for(String char2Comp : char2CompArray)
                    {
                        compIndex = condition.indexOf(char2Comp);
                        if(compIndex != -1)
                        {
                            comparator = char2Comp;
                            break;
                        }
                    }

                    if(compIndex == -1)
                    {
                        for(String char1Comp : char1CompArray)
                        {
                            compIndex = condition.indexOf(char1Comp);
                            if(compIndex != -1)
                            {
                                comparator = char1Comp;
                                break;
                            }
                        }
                    }

                    if(compIndex == -1) throw new ExpectedTokenException("expected \"=\", \">=\", \"<=\", \"<\" or \">\"", i+1, key.getPath());

                    if(condition.matches("[a-zA-Z0-9]+" + comparator + "[a-zA-Z0-9]+"))
                    {
                        String[] args = condition.split(comparator);
                        boolean secArgIsNum;
                        try { Integer.parseInt(args[1]); secArgIsNum = true;}
                        catch(NumberFormatException ignored) { secArgIsNum = false; }
                        //ex: execute if score foo vars = bar vars run function helloworldifs:if0
                        if(secArgIsNum)
                            command += "score " + args[0] + " vars matches " + args[1] + " run function " + Main.project.toLowerCase() + "unlesss:" + node.getName();
                        else
                            command += "score " + args[0] + " vars " + comparator + " " + args[1] + " vars" + " run function " + Main.project.toLowerCase() + "unlesss:" + node.getName();
                    }
                    else if(condition.matches("[a-zA-Z0-9]+=§[a-z_]+§(\\[([a-zA-Z0-9]+=[a-zA-Z0-9]+,)*[a-zA-Z0-9]+=[a-zA-Z0-9]+])?(\\{[\\s\\S]+})?"))
                    {
                        String[] sides = new String[]{condition.substring(0, condition.indexOf('=')), condition.substring(condition.indexOf('=')+1)};
                        String block = sides[0];
                        if(!blocks.containsKey(block)) throw new UnexpectedTokenException("block variable " + block + " does not exist!", i+1, key.getPath());
                        NumberGroup group = blocks.get(block);
                        String type = sides[1].substring(1, condition.indexOf('§', 1));
                        int nbt = condition.indexOf('{');
                        int state = condition.indexOf('[');
                        String jsonText = "";
                        String stateText = "";

                        if(nbt != -1)
                        {
                            jsonText = condition.substring(nbt, condition.length());
                            try { new ObjectMapper().readTree(jsonText); }
                            catch (JsonProcessingException e) { throw new WrappedCompilationException(i+1, e, key.getPath()); }
                        }

                        if(state != -1 && state < nbt)
                        {
                            stateText = condition.substring(state, condition.indexOf(']')+1);
                        }

                        command += group.get(0) + " " + group.get(1) + " " + group.get(2) + " " + type + jsonText + stateText
                                + " run function " + Main.project.toLowerCase() + "unlesss:" + node.getName();
                    }
                    else
                    {
                        throw new UnknownStatementException("Not a known syntax for command \"unless\": " + line, i+1, key.getPath());
                    }

                    virtualUnlesss.put(node, ifCode);
                    compiled.add(command);
                }
            }
            catch(CompilationException e)
            {
                if(e.isError())
                    errors.add(e.getMessage());
                else
                    warnings.add(e.getMessage());
            }
            catch(Throwable t)
            {
                //t.printStackTrace();
                Log.e("An unknown exception occured!", t);
                try { throw new WrappedCompilationException(i+1, t, key.getPath()); }
                catch(WrappedCompilationException e)
                {
                    errors.add(e.getMessage());
                }
            }
        }

        return compiled;
    }

    /**
     * This method is called when exporting a projecto to a directory
     * which is already holding a compiled version of the project.
     * To ensure that there are no leftovers from an older version of
     * the project, all the files are wiped clean first
     *
     * @param dir the root of the project
     */
    private static void deleteChildren(File dir)
    {
        File[] children = dir.listFiles();

        if(children != null)
        {
            for(File child : children)
            {
                if(child.isDirectory())
                    deleteChildren(child);
                else
                    child.delete();
            }
        }

        dir.delete();
    }

    /**
     * Resets all the lists and variables to start another
     * compilation session
     */
    private static void deinit()
    {
        Log.i("Deinitializing compiler");
        mcfunctionCode.clear();
        filePaths.clear();
        presentPaths.clear();
        nodePathMap.clear();
        root = null;
        rootNode = null;
        failed = false;
        errors.clear();
        warnings.clear();
        virtualIfs.clear();
        virtualUnlesss.clear();
        compiledVirtualCode.clear();
        VirtualIfNode.precedingIfs = 0;
        VirtualUnlessNode.precedingUnlesss = 0;
    }
}


<|start_filename|>src/io/github/keheck/Main.java<|end_filename|>
package io.github.keheck;

import io.github.keheck.project.saveandload.ConfigValue.ValueType;
import io.github.keheck.util.Directories;
import io.github.keheck.util.Log;
import io.github.keheck.util.OSUtils;
import io.github.keheck.window.*;
import io.github.keheck.tree.NavTreeFile;

import javax.swing.*;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import javax.swing.tree.DefaultTreeModel;
import java.awt.*;
import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;

public class Main
{
    public static JFrame frame;
    private static JPanel nav;
    public static JScrollPane editContainer;
    public static JTextArea editArea;
    private static JSplitPane splitPane;
    private static JMenuBar menuBar;
    public static JTree navTree;

    private static boolean killed = false;

    public static HashMap<NavTreeFile, ArrayList<String>> texts = new HashMap<>();
    public static final HashMap<String, ValueType> keyTypes = new HashMap<>();
    public static final HashMap<String, Method> valueSetters = new HashMap<>();

    public static String project;

    public static void main(String[] args)
    {
        System.out.println("Starting init...");
        OSUtils.init();
        Directories.initDirs();

        if(new File(Directories.rootDir, "runtime.txt.lck").exists())
        {
            System.out.println("Instance already running! Commiting ded...");
            killed = true;
            System.exit(1);
        }

        Log.init();
        Icons.init();

        Log.f1("Initialization finished!");
        Log.f2("Logger will be used from here on out...");
        Log.f1("Setting up UI...");

        try { UIManager.setLookAndFeel(new NimbusLookAndFeel()); }
        catch (UnsupportedLookAndFeelException e) { e.printStackTrace(); }

        frame = new JFrame("Minecraft Datapack Creator");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setMinimumSize(new Dimension(900, 750));
        frame.setLocationRelativeTo(null);
        menuBar = MainMenu.setupMenu();
        nav = NavigatorPanel.setupNavigatorPanel();
        navTree = new JTree();
        navTree.setModel(new DefaultTreeModel(null));
        nav.add(navTree);
        editContainer = (JScrollPane)EditPanel.setupEditTextArea()[0];
        editArea = (JTextArea)EditPanel.setupEditTextArea()[1];
        nav.setBackground(new Color(255, 255, 255));
        splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, nav, editContainer);
        splitPane.setEnabled(true);
        splitPane.setOneTouchExpandable(false);
        frame.add(splitPane);
        Tasks.onNewProject("project", "name", null);
        frame.setVisible(true);

        Log.f1("Setup UI!");
        Log.i("Setting up metadata handlers");

        keyTypes.put("keepComments", ValueType.BOOL);
        keyTypes.put("useVanilla", ValueType.BOOL);

        try
        {
            Method methKeepComments = MainMenu.class.getDeclaredMethod("setUseComments", boolean.class);
            Method methUseCommands = MainMenu.class.getDeclaredMethod("setUseVanilla", boolean.class);

            valueSetters.put("keepComments", methKeepComments);
            valueSetters.put("useVanilla", methUseCommands);
        }
        catch (NoSuchMethodException e)
        {
            Log.e("Failed to load method!", e);
            e.printStackTrace();
        }
    }
}


<|start_filename|>src/io/github/keheck/project/saveandload/Save.java<|end_filename|>
package io.github.keheck.project.saveandload;

import io.github.keheck.Main;
import io.github.keheck.util.Directories;
import io.github.keheck.util.Log;
import io.github.keheck.tree.AbstractNavTreeNode;
import io.github.keheck.tree.NavTreeFile;
import io.github.keheck.tree.NavTreeFolder;
import io.github.keheck.window.MainMenu;
import io.github.keheck.window.dialogs.DialogProjectSaved;

import javax.swing.*;
import javax.swing.tree.TreeNode;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;

public class Save
{
    /**
     * Saves the root directory and moves further on
     * @param project the name of the root directory
     */
    public static void save(String project)
    {
        cleanDirtyFile();

        File root = new File(Directories.projectsDir, project);
        Log.i("Starting saving process of " + project);
        try
        {
            if(!root.exists())
                root.mkdirs();

            AbstractNavTreeNode nodeRoot = (AbstractNavTreeNode)Main.navTree.getModel().getRoot();
            Enumeration<TreeNode> namespaces = nodeRoot.children();

            while(namespaces.hasMoreElements())
            {
                TreeNode space = namespaces.nextElement();

                if(space instanceof NavTreeFolder)
                {
                    handleFolder((NavTreeFolder)space, root);
                }
                else if(space instanceof NavTreeFile)
                {
                    handleFile((NavTreeFile)space, root);
                }
            }

            File projMeta = new File(root, "settings.meta");
            projMeta.createNewFile();
            String keepComments = "keepComments=" + MainMenu.keepComments.getState();
            String useCommands = "useVanilla=" + MainMenu.commandsOnly.getState();
            Files.write(projMeta.toPath(), (keepComments + System.lineSeparator() + useCommands).getBytes(), StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
        }
        catch (IOException e)
        {
            Log.e("Failed to save project " + project + ":", e);
            e.printStackTrace();
        }

        Log.i("Finished saving process! Project is saved at " + root.getAbsolutePath());
        new DialogProjectSaved(root.getAbsolutePath());
    }

    /**
     * Handles any folder encountered
     * @param node The node representing a folder
     * @param parentDir The parent directory of the foler
     * @throws IOException when an I/O error occurs
     */
    private static void handleFolder(NavTreeFolder node, File parentDir) throws IOException
    {
        Log.f2("Handling folder " + node.toString() + " and it's children");
        Enumeration<TreeNode> children = node.children();
        File currentDir = new File(parentDir, node.getName());
        currentDir.mkdirs();

        while(children.hasMoreElements())
        {
            TreeNode child = children.nextElement();

            if(child instanceof NavTreeFolder)
            {
                handleFolder((NavTreeFolder)child, currentDir);
            }
            else if(child instanceof NavTreeFile)
            {
                handleFile((NavTreeFile)child, currentDir);
            }
        }
    }

    /**
     * Handles any file encountered
     * @param node The node representing a file
     * @param parentDir the parent directory of the file
     * @throws IOException when an I/O error occurs
     */
    private static void handleFile(NavTreeFile node, File parentDir) throws IOException
    {
        Log.f2("Handling file " + node.toString());
        File fileDir = new File(parentDir, node.toString());
        ArrayList<String> lines = Main.texts.get(node);
        String wholeText = String.join(System.lineSeparator(), lines.toArray(new String[0]));

        if(!fileDir.exists())
            fileDir.createNewFile();

        Files.write(fileDir.toPath(), wholeText.getBytes());
    }

    /**
     * Clears the difference between the currently opened file
     * and the text mapped to the node representing the file
     */
    public static void cleanDirtyFile()
    {
        Log.f1("Cleaning the currently opened file...");
        JTextArea area = Main.editArea;
        AbstractNavTreeNode dirty = (AbstractNavTreeNode)Main.navTree.getLastSelectedPathComponent();

        //Will return true if the node represents a file
        if(dirty != null && dirty.getType().isLeaf())
        {
            ArrayList<String> savedText = Main.texts.get(dirty);
            ArrayList<String> actualText = new ArrayList<>(Arrays.asList(area.getText().split("\n")));

            if(!savedText.equals(actualText))
            {
                Log.f2("Opened file is dirty! GET CLEANED!!!");
                savedText.clear();
                savedText.addAll(actualText);
            }

            Main.texts.put((NavTreeFile)dirty, savedText);
        }
    }
}


<|start_filename|>src/io/github/keheck/window/dialogs/DialogNewProject.java<|end_filename|>
package io.github.keheck.window.dialogs;

import io.github.keheck.Main;
import io.github.keheck.Tasks;
import io.github.keheck.util.Log;

import javax.swing.*;
import java.awt.*;

public class DialogNewProject extends JDialog
{
    private JTextField projName;
    private JTextField nameName;
    private JButton approve;
    private JButton cancel;

    public DialogNewProject()
    {
        super(Main.frame, "New Project");
        JPanel panel = new JPanel();
        this.add(panel);

        Log.i("Opening dialog DialogNewProject");

        GroupLayout layout = new GroupLayout(panel);
        panel.setLayout(layout);

        layout.setAutoCreateGaps(true);
        layout.setAutoCreateContainerGaps(true);

        JLabel projLabel = new JLabel("Project Name");
        JLabel nameLabel = new JLabel("Namespace");

        projName = new JTextField();
        nameName = new JTextField();

        approve = new JButton("Create");
        cancel = new JButton("Cancel");

        layout.setHorizontalGroup(
                layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                        .addGroup(layout.createSequentialGroup()
                                .addComponent(approve)
                                .addComponent(cancel))
                        .addGroup(layout.createSequentialGroup()
                                .addComponent(projLabel)
                                .addComponent(projName))
                        .addGroup(layout.createSequentialGroup()
                                .addComponent(nameLabel)
                                .addGap(13)
                                .addComponent(nameName))
        );
        layout.setVerticalGroup(
                layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                                .addComponent(projLabel)
                                .addComponent(projName, 30, 30, 30))
                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                                .addComponent(nameLabel)
                                .addComponent(nameName, 30, 30, 30))
                        .addGap(30)
                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                                .addComponent(approve)
                                .addComponent(cancel))
        );

        setMinimumSize(new Dimension(400, 200));
        setResizable(false);
        setLocationRelativeTo(null);
        setupActions();
        setVisible(true);
    }

    private void setupActions()
    {
        cancel.addActionListener(e -> dispose());
        approve.addActionListener(e -> Tasks.onNewProject(projName.getText(), nameName.getText(), this));
    }
}


<|start_filename|>src/io/github/keheck/tree/VirtualUnlessNode.java<|end_filename|>
package io.github.keheck.tree;

import io.github.keheck.Main;

import java.util.ArrayList;

public class VirtualUnlessNode extends NavTreeFile
{
    public static int precedingUnlesss = 0;

    public VirtualUnlessNode()
    {
        super(NodeType.MCFUNCTION, "unless" + precedingUnlesss);
        precedingUnlesss++;
    }

    @Override
    public String getPath() { return Main.project + "unlesss/" + toString(); }
}


<|start_filename|>src/io/github/keheck/util/Constants.java<|end_filename|>
package io.github.keheck.util;

public interface Constants
{
    String DIRECTORY = "datapackCreator";
}


<|start_filename|>src/io/github/keheck/util/Util.java<|end_filename|>
package io.github.keheck.util;

import org.apache.commons.lang3.ArrayUtils;

import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;

public class Util
{
    public static void safeClose(Closeable closeable)
    {
        if(closeable != null)
        {
            try
            {
                closeable.close();
            }
            catch (IOException e)
            {
                Log.e("Failed to close: " + closeable.getClass().getSimpleName() + ": " + e.getMessage());
            }
        }
    }

    /**
     * Returns an array holding all indices of occurences of {@code toSearch}
     * in {@code src}
     *
     * @param src the source string
     * @param toSearch the string to look out for
     * @return an {@code int[]} holding all indices of the occurence,
     *         {@code null} if no occurence could be found
     */
    public static int[] getAllIndicesOf(String src, String toSearch)
    {
        if(!src.contains(toSearch)) return new int[]{-1};

        ArrayList<Integer> indices = new ArrayList<>();

        for(int i = src.indexOf(toSearch); i != -1; i = src.indexOf(toSearch, i+1)) indices.add(i);

        int[] primitiveIndices = ArrayUtils.toPrimitive(indices.toArray(new Integer[0]));
        Arrays.sort(primitiveIndices);
        return primitiveIndices;
    }

    public static int[] getAllIndicesOf(String src, char toSearch)
    {
        return getAllIndicesOf(src, new String(new char[]{toSearch}));
    }
}


<|start_filename|>src/io/github/keheck/window/dialogs/DialogLoadProject.java<|end_filename|>
package io.github.keheck.window.dialogs;

import io.github.keheck.Main;
import io.github.keheck.project.saveandload.Load;
import io.github.keheck.util.Directories;
import io.github.keheck.util.Log;

import javax.swing.*;
import java.awt.*;

public class DialogLoadProject extends JDialog
{
    private JFileChooser fileChooser;

    public DialogLoadProject()
    {
        super(Main.frame, "Load project...");

        Log.i("Showing dialog DialogLoadProject");
        fileChooser = new JFileChooser(Directories.projectsDir);
        fileChooser.setFileFilter(new DialogLoadProjFilter());
        fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        JPanel panel = new JPanel();
        panel.add(fileChooser);
        add(panel);

        setLocationRelativeTo(null);
        setMinimumSize(new Dimension(600, 400));
        setResizable(false);
        setupActions();
        setVisible(true);
    }

    private void setupActions()
    {
        fileChooser.addActionListener(e ->
        {
            if(e.getActionCommand().equals("ApproveSelection"))
                Load.load(fileChooser.getSelectedFile());
            setVisible(false);
        });
    }
}


<|start_filename|>src/io/github/keheck/project/exceptions/SyntaxException.java<|end_filename|>
package io.github.keheck.project.exceptions;

public class SyntaxException extends CompilationException
{
    public SyntaxException(String syntaxError, int lineNum, String function)
    {
        super(lineNum, syntaxError, function, true);
    }
}


<|start_filename|>src/io/github/keheck/util/Directories.java<|end_filename|>
package io.github.keheck.util;

import java.io.File;

public class Directories
{
    public static File projectsDir;
    public static File rootDir;
    public static File mcDir;
    public static File logDir;
    public static File crashDir;

    public static void initDirs()
    {
        rootDir = OSUtils.dataDir;
        mcDir = OSUtils.mcDir;

        projectsDir = new File(rootDir, "projects");
        if(!projectsDir.exists()) projectsDir.mkdirs();

        logDir = new File(rootDir, "logs");
        if(!logDir.exists()) logDir.mkdirs();

        crashDir = new File(rootDir, "crash_logs");
        if(!crashDir.exists()) crashDir.mkdirs();
    }
}


<|start_filename|>src/io/github/keheck/tree/NavTreeFile.java<|end_filename|>
package io.github.keheck.tree;

import javax.swing.tree.MutableTreeNode;

public class NavTreeFile extends AbstractNavTreeNode
{
    private MutableTreeNode parent;
    private Object user;

    public NavTreeFile(NodeType type, String name) { super(type, name); }

    @Override
    public boolean isLeaf() { return true; }

    @Override
    public void setUserObject(Object object) { this.user = object; }

    @Override
    public void removeFromParent()
    {
        parent.remove(this);
        parent = null;
    }

    @Override
    public void setParent(MutableTreeNode newParent) { this.parent = newParent; }

    @Override
    public void insert(MutableTreeNode child, int index) { }

    @Override
    public void remove(int index) { }

    @Override
    public void remove(MutableTreeNode node) { }
}


<|start_filename|>src/io/github/keheck/window/Icons.java<|end_filename|>
package io.github.keheck.window;

import io.github.keheck.util.FileUtils;
import io.github.keheck.util.Log;
import io.github.keheck.util.Util;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

public class Icons
{
    public static ImageIcon MISSING_ICON;

    public static ImageIcon FILE_MCFUNC;
    public static ImageIcon FILE_JSON;
    public static ImageIcon FILE_NAMESPACE;
    public static ImageIcon FILE_NEWFILE;
    public static ImageIcon FILE_REMOVEFILE;
    public static ImageIcon FILE_FOLDER;
    public static ImageIcon FILE_METADATA;
    public static ImageIcon FILE_UNKNOWN;

    public static ImageIcon PROJECT_EXPORT;
    public static ImageIcon PROJECT_NEW;
    public static ImageIcon PROJECT_SAVE;
    public static ImageIcon PROJECT_LOAD;

    public static ImageIcon TREE_EXPAND;
    public static ImageIcon TREE_COLLAPSE;

    public static void init()
    {
        MISSING_ICON = loadIcon("missing_icon.png");

        FILE_MCFUNC = loadIcon("files/mcfunction.png");
        FILE_JSON = loadIcon("files/json.png");
        FILE_NAMESPACE = loadIcon("files/namespace.png");
        FILE_NEWFILE = loadIcon("files/add_file.png");
        FILE_REMOVEFILE = loadIcon("files/remove_file.png");
        FILE_FOLDER = loadIcon("files/folder.png");
        FILE_METADATA = loadIcon("files/metadata.png");
        FILE_UNKNOWN = loadIcon("files/unknown_file.png");

        PROJECT_EXPORT = loadIcon("project/export.png");
        PROJECT_NEW = loadIcon("project/new_project.png");
        PROJECT_SAVE = loadIcon("project/save_project.png");
        PROJECT_LOAD = loadIcon("project/load_project.png");

        TREE_EXPAND = loadIcon("tree/expand.png");
        TREE_COLLAPSE = loadIcon("tree/collapse.png");
    }

    private static ImageIcon loadIcon(String path)
    {
        Log.i("Loading image " + path);
        String imagePath = "/icons/";
        BufferedImage image;
        InputStream stream = null;

        try
        {
            stream = FileUtils.getResource(imagePath + path);
            image = ImageIO.read(stream);
            return new ImageIcon(image);
        }
        catch (IOException e)
        {
            Log.e("Failed to load: " + imagePath + path, e);
        }
        finally
        {
            Util.safeClose(stream);
        }

        return null;
    }
}


<|start_filename|>src/io/github/keheck/window/dialogs/DialogNewFolder.java<|end_filename|>
package io.github.keheck.window.dialogs;

import io.github.keheck.Main;
import io.github.keheck.Tasks;
import io.github.keheck.tree.AbstractNavTreeNode;
import io.github.keheck.util.Log;

import javax.swing.*;
import java.awt.*;

public class DialogNewFolder extends JDialog
{
    private JTextField field;
    private JButton approve;
    private JButton cancel;
    private AbstractNavTreeNode parent;

    public DialogNewFolder(AbstractNavTreeNode parent)
    {
        super(Main.frame, "New Folder");
        JPanel host = new JPanel();
        add(host);

        Log.i("Opening dialog DialogNewFolder");
        JLabel label = new JLabel("Folder");
        field = new JTextField();
        this.parent = parent;
        approve = new JButton("Ok");
        cancel = new JButton("Cancel");

        GroupLayout layout = new GroupLayout(host);
        host.setLayout(layout);

        layout.setAutoCreateGaps(true);
        layout.setAutoCreateContainerGaps(true);

        layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                .addGroup(layout.createSequentialGroup()
                        .addComponent(label)
                        .addComponent(field))
                .addGroup(layout.createSequentialGroup()
                        .addComponent(approve)
                        .addComponent(cancel))
        );

        layout.setVerticalGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                        .addComponent(label)
                        .addComponent(field, 30, 30, 30))
                .addGap(70)
                .addGroup(layout.createParallelGroup()
                        .addComponent(approve)
                        .addComponent(cancel))
        );

        setMinimumSize(new Dimension(400, 200));
        setResizable(false);
        setLocationRelativeTo(null);
        setupActions();
        setVisible(true);
    }

    private void setupActions()
    {
        cancel.addActionListener(e -> dispose());
        approve.addActionListener(e -> Tasks.createFolder(field.getText(), this, parent));
    }
}


<|start_filename|>src/io/github/keheck/tree/VirtualIfNode.java<|end_filename|>
package io.github.keheck.tree;

import io.github.keheck.Main;

import java.util.ArrayList;

public class VirtualIfNode extends NavTreeFile
{
    public static int precedingIfs = 0;

    public VirtualIfNode()
    {
        super(NodeType.MCFUNCTION, "if" + precedingIfs);
        precedingIfs++;
    }

    @Override
    public String getPath() { return Main.project + "ifs/" + toString(); }
}


<|start_filename|>src/io/github/keheck/window/EditPanel.java<|end_filename|>
package io.github.keheck.window;

import io.github.keheck.Main;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class EditPanel
{

    public static JComponent[] setupEditTextArea()
    {
        JTextArea area = new JTextArea();
        area.setEditable(false);
        JScrollPane scroll = new JScrollPane();
        scroll.setViewportView(area);
        scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
        Font mono = new Font("Monospaced", Font.PLAIN, 12);
        area.setFont(mono);
        area.setTabSize(5);
        area.setEnabled(false);

        return new JComponent[]{scroll, area};
    }

    public static void setupActions(JTextArea area)
    {
        area.addKeyListener(new KeyListener()
        {
            @Override public void keyTyped(KeyEvent e) { }

            @Override public void keyPressed(KeyEvent e) { }

            @Override
            public void keyReleased(KeyEvent e)
            {
                if(e.getKeyCode() == KeyEvent.VK_TAB)
                {
                    int caretPos = Main.editArea.getCaretPosition();
                    Main.editArea.setText(Main.editArea.getText().replaceAll("\t", "    "));
                    try { Main.editArea.setCaretPosition(caretPos+3); }
                    catch (IllegalArgumentException ex) { Main.editArea.setCaretPosition(Main.editArea.getText().length()-1); }
                }
            }
        });
    }
}


<|start_filename|>src/io/github/keheck/project/compiling/PostCompiler.java<|end_filename|>
package io.github.keheck.project.compiling;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import io.github.keheck.Main;
import io.github.keheck.tree.NavTreeFile;
import io.github.keheck.tree.NavTreeFolder;
import io.github.keheck.tree.VirtualIfNode;
import io.github.keheck.tree.VirtualUnlessNode;
import io.github.keheck.util.Log;
import io.github.keheck.window.NavTree;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Set;

import static io.github.keheck.project.compiling.Compiler.*;

class PostCompiler
{
    /**
     * Writes the .mcmeta file
     */
    static void writeMCMeta()
    {
        Log.i("Writing mcmeta file...");
        flushText(NavTree.meta);
    }

    /**
     * Writes all .json files
     */
    static void writeJsons()
    {
        Log.i("Writing all json files...");
        Set<NavTreeFile> jsons = filePaths.keySet();

        for(NavTreeFile file : jsons)
        {
            if(file.getType().getExtension().equals(".json"))
            {
                try
                {
                    Log.f1("Writing json file " + file.getPath());

                    JsonParser parser = new JsonParser();
                    String json = String.join("\n", Main.texts.get(file));
                    parser.parse(json);
                }
                catch(JsonSyntaxException e)
                {
                    Throwable cause = e.getCause() != null ? e.getCause() : e;
                    Log.e("Failed to write " + file.getPath() + ": ", e);
                    errors.add(cause.getMessage());
                    continue;
                }

                flushText(file);
            }
        }
    }

    /**
     * Writes all .mcfunction files
     */
    static void writeMCFunctions()
    {
        Log.i("Flushing functions...");
        Set<NavTreeFile> functions = filePaths.keySet();

        for(NavTreeFile nodeFile : functions)
        {
            if(nodeFile.getType().getExtension().equals(".mcfunction"))
            {
                Log.f1("Writing mcfunction file " + nodeFile.getPath());
                ArrayList<NavTreeFolder> path = filePaths.get(nodeFile);

                if(!presentPaths.contains(path))
                {
                    if(path.size() == 0)
                    {
                        try
                        {
                            File file = new File(root, nodeFile.toString());
                            file.createNewFile();
                            ArrayList<String> lines = mcfunctionCode.get(nodeFile);
                            String text = String.join("\n", lines);
                            Files.write(file.toPath(), text.getBytes(), StandardOpenOption.APPEND);
                        }
                        catch (IOException e)
                        {
                            e.printStackTrace();
                        }
                    }
                    else
                    {
                        File file = new File(root, "data");
                        file = new File(file, path.get(0).getName());

                        for(int i = 1; i < path.size(); i++)
                            file = new File(file, path.get(i).getName());

                        file.mkdirs();
                        presentPaths.add(path);
                        nodePathMap.put(getStringKeyFor(path), file);

                        try
                        {
                            file = new File(file, nodeFile.toString());
                            file.createNewFile();
                            ArrayList<String> lines = mcfunctionCode.get(nodeFile);
                            String text = String.join("\n", lines);
                            Files.write(file.toPath(), text.getBytes(), StandardOpenOption.APPEND);
                        }
                        catch (IOException e)
                        {
                            e.printStackTrace();
                        }
                    }
                }
                else
                {
                    try
                    {
                        File file = new File(nodePathMap.get(getStringKeyFor(path)), nodeFile.toString());
                        file.createNewFile();
                        ArrayList<String> lines = mcfunctionCode.get(nodeFile);
                        String text = String.join("\n", lines);
                        Files.write(file.toPath(), text.getBytes(), StandardOpenOption.APPEND);
                    }
                    catch (IOException e)
                    {
                        Log.e("And error occured: ", e);
                        e.printStackTrace();
                    }
                }
            }
        }

        try
        {
            File loadJson = new File(root, "data/minecraft/tags/functions");
            File initFunc = new File(root, "data/" + Main.project + "init/functions");
            loadJson.mkdirs();
            initFunc.mkdirs();
            loadJson = new File(loadJson, "load.json");
            initFunc = new File(initFunc, "comp_init.mcfunction");
            loadJson.createNewFile();
            initFunc.createNewFile();

            FileWriter writer = new FileWriter(loadJson);

            JsonFactory factory = new JsonFactory();
            JsonGenerator gen = factory.createGenerator(writer);
            gen.useDefaultPrettyPrinter();
            gen.writeStartObject();
                gen.writeArrayFieldStart("values");
                    gen.writeString(Main.project + "init:comp_init");
                gen.writeEndArray();
            gen.writeEndObject();
            gen.flush();
            gen.close();

            writer = new FileWriter(initFunc);
            writer.write("scoreboard objectives add vars dummy");
            writer.flush();
            writer.close();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }

    static void writeVirtuals()
    {
        Log.i("Writing virtual nodes...");
        Set<NavTreeFile> keys = Compiler.compiledVirtualCode.keySet();

        for(NavTreeFile key : keys)
        {
            if(key instanceof VirtualIfNode)
            {
                try
                {
                    Log.f1("Writing virtual if " + key.getPath());
                    File file = new File(root, "data/" + Main.project + "ifs/");
                    file.mkdirs();
                    file = new File(file, key.toString());
                    file.createNewFile();

                    String text = String.join("\n", compiledVirtualCode.get(key));
                    Files.write(file.toPath(), text.getBytes(), StandardOpenOption.APPEND);
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
            else if(key instanceof VirtualUnlessNode)
            {
                try
                {
                    Log.f1("Writing virtual unless " + key.getPath());

                    File file = new File(root, "data/" + Main.project + "unlesss/");
                    file.mkdirs();
                    file = new File(file, key.toString());
                    file.createNewFile();

                    String text = String.join("\n", compiledVirtualCode.get(key));
                    Files.write(file.toPath(), text.getBytes(), StandardOpenOption.APPEND);
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * Flushes the text associated with the given tree node
     * Is only used for the .mcmeta file and the .json
     * files because the text of mcfunction files is simplified
     * and changed before the flushing.
     *
     * @param nodeFile the file whose text is being flushed
     */
    private static void flushText(NavTreeFile nodeFile)
    {
        ArrayList<NavTreeFolder> path = filePaths.get(nodeFile);

        if(!presentPaths.contains(path))
        {
            if(nodeFile.getType().getExtension().equals(".mcmeta"))
            {
                try
                {
                    File file = new File(root, nodeFile.toString());
                    file.createNewFile();
                    ArrayList<String> lines = Main.texts.get(nodeFile);
                    String text = String.join("\n", lines.toArray(new String[0]));
                    Files.write(file.toPath(), text.getBytes(), StandardOpenOption.APPEND);
                }
                catch (IOException e)
                {
                    Log.e("An I/O error occured while writing " + nodeFile.getPath() + ":", e);
                    e.printStackTrace();
                }
            }
            else
            {
                File file = new File(root, "data");
                file = new File(file, path.get(0).getName());

                for(int i = 1; i < path.size(); i++)
                    file = new File(file, path.get(i).getName());

                file.mkdirs();
                presentPaths.add(path);
                nodePathMap.put(getStringKeyFor(path), file);

                try
                {
                    file = new File(file, nodeFile.toString());
                    file.createNewFile();
                    ArrayList<String> lines = Main.texts.get(nodeFile);
                    String text = String.join("\n", lines.toArray(new String[0]));
                    Files.write(file.toPath(), text.getBytes(), StandardOpenOption.APPEND);
                }
                catch (IOException e)
                {
                    Log.e("An I/O error occured while writing " + nodeFile.getPath() + ":", e);
                    e.printStackTrace();
                }
            }
        }
        else
        {
            try
            {
                File file = new File(nodePathMap.get(getStringKeyFor(path)), nodeFile.toString());
                file.createNewFile();
                ArrayList<String> lines = Main.texts.get(nodeFile);
                String text = String.join("\n", lines.toArray(new String[0]));
                Files.write(file.toPath(), text.getBytes(), StandardOpenOption.APPEND);
            }
            catch (IOException e)
            {
                Log.e("An I/O error occured while writing " + nodeFile.getPath() + ":", e);
                e.printStackTrace();
            }
        }
    }

    /**
     * builds a key of type {@code String} representing the path
     * relative to the project root to clearly identify a file incase
     * there are two files with the same name in different directories
     *
     * @param path the path of the file
     * @return the {@code String} representing the path
     */
    private static String getStringKeyFor(ArrayList<NavTreeFolder> path)
    {
        StringBuilder builder = new StringBuilder(rootNode.getName());

        if(path.size() != 0)
        {
            builder.append("/");

            for(NavTreeFolder folder : path)
                builder.append(folder);
        }

        return builder.toString();
    }
}


<|start_filename|>src/io/github/keheck/project/saveandload/Load.java<|end_filename|>
package io.github.keheck.project.saveandload;

import io.github.keheck.Main;
import io.github.keheck.util.Log;
import io.github.keheck.window.NavTree;
import io.github.keheck.window.dialogs.DialogLoadProject;
import io.github.keheck.tree.AbstractNavTreeNode.NodeType;
import io.github.keheck.tree.NavTreeFile;
import io.github.keheck.tree.NavTreeFolder;

import javax.swing.tree.DefaultTreeModel;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

public class Load
{
    private static DefaultTreeModel model;
    private static NavTreeFolder rootNode;

    public static void initLoad()
    {
        new DialogLoadProject();
        model = (DefaultTreeModel)Main.navTree.getModel();
    }

    public static void load(File root)
    {
        Log.i("Loading project at " + root.getPath() + "...");
        Main.texts.clear();
        Main.project = root.getName();
        HashMap<String, ConfigValue> configValues = new HashMap<>();

        File[] files = root.listFiles();
        try
        {
            if(files != null)
            {
                rootNode = new NavTreeFolder(NodeType.FOLDER, root.getName());
                model.setRoot(rootNode);

                for(File f : files)
                {
                    if(f.getName().equals("settings.meta"))
                    {
                        BufferedReader reader = new BufferedReader(new FileReader(f));

                        ArrayList<String> lines = new ArrayList<>();
                        String fline;
                        while((fline = reader.readLine()) != null) lines.add(fline);

                        reader.close();

                        for(String line : lines)
                        {
                            String key = line.split("=")[0];
                            ConfigValue.ValueType type = Main.keyTypes.get(key);

                            switch (type)
                            {
                                case BOOL:
                                    Boolean value = Boolean.parseBoolean(line.split("=")[1]);
                                    try { Main.valueSetters.get(key).invoke(Load.class, value); }
                                    catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); }
                                    break;
                            }
                        }

                        continue;
                    }

                    if(f.isDirectory())
                    {
                        addFolderNode(rootNode, new NavTreeFolder(NodeType.NAMESPACE, f.getName()), f);
                    }
                    else
                    {
                        String[] splitFileName = f.getName().split("\\.");
                        NavTreeFile treeFile = new NavTreeFile(NodeType.getNodeType("." + splitFileName[1]), splitFileName[0]);
                        addFileNode(rootNode, treeFile, f);
                    }
                }
            }
        }catch (IOException e)
        {
            Log.e("An I/O error occured while loading " + root.getPath() + ":", e);
            e.printStackTrace();
        }

        model = null;
    }

    private static void addFolderNode(NavTreeFolder parent, NavTreeFolder child, File childDir) throws IOException
    {
        model.insertNodeInto(child, parent, NavTree.getChildIndex(parent.children(), child));

        File[] files = childDir.listFiles();

        if(files != null)
        {
            for(File file : files)
            {
                if(file.isDirectory())
                {
                    addFolderNode(child, new NavTreeFolder(NodeType.FOLDER, file.getName()), file);
                }
                else
                {
                    String[] splitFileName = file.getName().split("\\.");
                    NavTreeFile treeFile = new NavTreeFile(NodeType.getNodeType("." + splitFileName[1]), splitFileName[0]);
                    addFileNode(child, treeFile, file);
                }
            }
        }
    }

    private static void addFileNode(NavTreeFolder parent, NavTreeFile child, File file) throws IOException
    {
        model.insertNodeInto(child, parent, NavTree.getChildIndex(parent.children(), child));
        byte[] data = Files.readAllBytes(file.toPath());
        String[] lines = new String(data).split(System.lineSeparator());
        ArrayList<String> lineList = new ArrayList<>(Arrays.asList(lines));
        Main.texts.put(child, lineList);
        if(child.getType().getExtension().equals(".mcmeta"))
            NavTree.meta = child;
    }
}


<|start_filename|>src/io/github/keheck/project/saveandload/ConfigValue.java<|end_filename|>
package io.github.keheck.project.saveandload;

import static io.github.keheck.project.saveandload.ConfigValue.ValueType.*;

public class ConfigValue
{
    private String value;
    private ValueType type;

    public ConfigValue(String value)
    {
        this.value = value;

        if(value.toLowerCase().matches("(true|false)"))
            type = BOOL;
        else if(value.matches("-?\\d*\\.\\d+"))
        {
            try
            {
                Float.parseFloat(value);
                type = FLOAT;
            }
            catch(NumberFormatException ignore) {}
        }
        else if(value.matches("-?\\d+"))
        {
            try
            {
                Integer.parseInt(value);
                type = INTEGER;
            }
            catch(NumberFormatException ignore) { }
        }
        else
            type = STRING;
    }

    public String getValue() { return value; }

    public ValueType getType() { return type; }

    public enum ValueType
    {
        STRING, BOOL, INTEGER, FLOAT
    }
}


<|start_filename|>src/io/github/keheck/window/dialogs/DialogDynamic.java<|end_filename|>
package io.github.keheck.window.dialogs;

import io.github.keheck.Main;
import io.github.keheck.util.Log;

import javax.swing.*;
import java.awt.*;

public class DialogDynamic extends JDialog
{
    private JLabel label;
    private JLabel task;
    private String currentTask;

    public DialogDynamic()
    {
        super(Main.frame, "Compiling...");
        Log.i("Opening dialog DialogDynamic");
        label = new JLabel("Compiling");
        task = new JLabel("What should I do?");

        JPanel host = new JPanel();
        this.add(host);

        GroupLayout layout = new GroupLayout(host);
        host.setLayout(layout);

        layout.setVerticalGroup(layout.createSequentialGroup()
                .addComponent(label)
                .addComponent(task));

        layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                .addComponent(label)
                .addComponent(task));

        setLocationRelativeTo(null);
        setResizable(false);
        setSize(new Dimension(200, 100));
        setVisible(true);
    }

    public void cycle()
    {
        String text = label.getText();

        switch(text)
        {
            case "Compiling":
                label.setText("Compiling.");
                break;
            case "Compiling.":
                label.setText("Compiling..");
                break;
            case "Compiling..":
                label.setText("Compiling...");
                break;
            case "Compiling...":
                label.setText("Compiling");
                break;
        }
    }

    public void updateTask(String newTask)
    {
        this.task.setText(newTask);
    }
}


<|start_filename|>src/io/github/keheck/util/FileUtils.java<|end_filename|>
package io.github.keheck.util;

import java.io.File;
import java.io.InputStream;

public class FileUtils
{
    public static InputStream getResource(String path) { return FileUtils.class.getResourceAsStream(path); }
}


<|start_filename|>src/io/github/keheck/window/dialogs/DialogErrorNameSame.java<|end_filename|>
package io.github.keheck.window.dialogs;

import io.github.keheck.util.Log;

import javax.swing.*;
import java.awt.*;

public class DialogErrorNameSame extends JDialog
{
    private JButton ok;

    public DialogErrorNameSame(JDialog owner)
    {
        super(owner, "ERROR");
        JPanel host = new JPanel();
        this.add(host);

        Log.i("Opening dialog DialogErrorNameSame");
        ok = new JButton("Ok");
        JLabel label = new JLabel("There's already a file with that name!");

        GroupLayout layout = new GroupLayout(host);
        host.setLayout(layout);

        layout.setVerticalGroup(layout.createSequentialGroup()
                .addComponent(label)
                .addComponent(ok));

        layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                .addComponent(label)
                .addComponent(ok));

        setLocationRelativeTo(null);
        setResizable(false);
        setSize(new Dimension(200, 100));
        setupActions();
        setVisible(true);
    }

    private void setupActions()
    {
        ok.addActionListener(e -> dispose());
    }
}


<|start_filename|>src/io/github/keheck/tree/NavTreeFolder.java<|end_filename|>
package io.github.keheck.tree;

import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Vector;

public class NavTreeFolder extends AbstractNavTreeNode
{
    private Vector<TreeNode> children;
    private MutableTreeNode parent;
    private Object user;

    public NavTreeFolder(NodeType type, String name)
    {
        super(type, name);
        children = new Vector<>();
    }

    @Override
    public TreeNode getChildAt(int childIndex) { return children.get(childIndex); }

    @Override
    public int getChildCount() { return children.size(); }

    @Override
    public int getIndex(TreeNode node) { return children.indexOf(node); }

    @Override
    public boolean isLeaf() { return children.size() == 0; }

    @Override
    public Enumeration<TreeNode> children() { return Collections.enumeration(children); }

    @Override
    public void insert(MutableTreeNode child, int index)
    {
        children.insertElementAt(child, index);
        child.setParent(this);
    }

    @Override
    public void remove(int index)
    {
        AbstractNavTreeNode node = (AbstractNavTreeNode)children.remove(index);
        node.setParent(null);
    }

    @Override
    public void remove(MutableTreeNode node) { children.remove(node); }

    @Override
    public void setUserObject(Object object) { this.user = object; }

    @Override
    public void removeFromParent()
    {
        parent.remove(this);
        this.parent = null;
    }

    @Override
    public void setParent(MutableTreeNode newParent) { this.parent = newParent; }
}


<|start_filename|>src/io/github/keheck/window/NavigatorPanel.java<|end_filename|>
package io.github.keheck.window;

import io.github.keheck.Main;
import org.jdesktop.swingx.JXFrame;

import javax.swing.*;
import java.awt.*;

public class NavigatorPanel
{
    public static JPanel setupNavigatorPanel()
    {
        JFrame frame = Main.frame;

        JScrollPane scrollPane = new JScrollPane();
        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
        scrollPane.add(panel);
        scrollPane.setMinimumSize(new Dimension(200, frame.getHeight()));
        scrollPane.setMaximumSize(new Dimension(400, frame.getHeight()));
        panel.setMinimumSize(new Dimension(200, frame.getHeight()));

        return panel;
    }
}


<|start_filename|>src/io/github/keheck/project/compiling/PreCompiler.java<|end_filename|>
package io.github.keheck.project.compiling;

import io.github.keheck.project.exceptions.CompilationException;
import io.github.keheck.project.exceptions.UnknownStatementException;
import io.github.keheck.project.exceptions.WrappedCompilationException;
import io.github.keheck.tree.NavTreeFile;
import io.github.keheck.util.Util;
import io.github.keheck.window.MainMenu;
import org.apache.commons.lang3.ArrayUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

/**
 * This class handles all the stuff required for the compilation process such as
 * <ul>
 *     <li>removing whitespaces</li>
 *     <li>simplifiying redundant code like if statements that are always true/false</li>
 * </ul>
 */

class PreCompiler
{
    static final String[] char2CompArray = {"<=", ">="};
    static final String[] char1CompArray = {">", "<", "="};
    private static final String[] operands = {"+=", "-=", "*=", "/=", "%=", "=", "><", ">", "<"};
    private static final String[] jsonStrings = {":", "[", "]", "{", "}", ","};

    static ArrayList<String> preCompile(NavTreeFile function, ArrayList<String> text)
    {
        ArrayList<String> trimmed = new ArrayList<>();
        for(String oldLine : text) trimmed.add(oldLine.trim());
        int lineNum = 0;
        //does not exist (somehow) OR is blank OR is a one-line comment AND "keep comments" is false
        trimmed.removeIf((s) -> s == null || s.matches("\\s*") || !MainMenu.keepComments.getState() && s.startsWith("#"));

        for(String line : trimmed)
        {
            try
            {
                String nonWhitespace = removeWhitespaces(line, function, lineNum);
                trimmed.set(lineNum, nonWhitespace);
            }
            catch(CompilationException e)
            {
                if(!e.getClass().equals(WrappedCompilationException.class))
                    e.printStackTrace();
                else
                    e.getCause().printStackTrace();

                if(e.isError())
                {
                    //Compilation will continue to find other errors, but won't export
                    //(Wait, isn't that how a compiler works and what it's supposed to do?)
                    Compiler.skippedFiles.add(function);
                    Compiler.errors.add(e.getMessage());
                    Compiler.failed = true;
                }
                else
                    Compiler.warnings.add(e.getMessage());
            }

            //BACKGROUND INFO: This came previously *before* the trimming... wasn't such a good idea
            lineNum++;
        }

        trimmed = simplify(trimmed);
        //trimmed.removeIf((s) -> s == null || s.matches("\\s*") || !MainMenu.keepComments.getState() && s.startsWith("#"));
        return trimmed;
    }

    /**
     * The new alhorythm for removing spaces to make the line comfortable to compile
     * NOT FROM DEV: Why don't I just remove all whitespaces? Because I can
     * @param line the line that is being adjusted
     * @param function the funtion that hosts the line
     * @param lineNum the line number
     * @return the adjusted string
     */
    private static String removeWhitespaces(String line, NavTreeFile function, int lineNum)
    {
        line = line.trim();
        ArrayList<Character> lineChars = arrayListFromString(line);
        String adjustedString;
        //a universal index for required indices in the line
        //is used for indices that are only temporary used to conserve resources
        int uniIndex = 0;

        if(line.matches("\\{") || line.matches("}")) return line;

        try
        {
            if(line.startsWith("run"))
            {
                while(lineChars.get(3).equals(' ')) lineChars.remove(3);
                while(lineChars.get(4).equals(' ')) lineChars.remove(4);
            }
            else if(line.startsWith("if"))
            {
                while(lineChars.get(2).equals(' ')) lineChars.remove(2);
                while(lineChars.get(3).equals(' ')) lineChars.remove(3);

                adjustedString = stringFromArrayList(lineChars);
                boolean lengthIs2 = false;

                //variable check
                if(adjustedString.matches("if\\([a-zA-Z0-9]+\\s*(<|<=|=|>=|>)\\s*[a-zA-Z0-9]+\\s*\\)(\\s*\\{)?"))
                {
                    for(String char2Comp : char2CompArray)
                    {
                        uniIndex = adjustedString.indexOf(char2Comp);
                        if(uniIndex != -1)
                        {
                            lengthIs2 = true;
                            break;
                        }
                    }

                    if(uniIndex == -1)
                    {
                        for(String char1Comp : char1CompArray)
                        {
                            uniIndex = adjustedString.indexOf(char1Comp);
                            if(uniIndex != -1) break;
                        }
                    }

                    if(lengthIs2) while(lineChars.get(uniIndex+2).equals(' ')) lineChars.remove(uniIndex+2);
                    else while(lineChars.get(uniIndex+1).equals(' ')) lineChars.remove(uniIndex+1);
                    for(;lineChars.get(uniIndex-1).equals(' ');uniIndex--) lineChars.remove(uniIndex-1);

                    adjustedString = stringFromArrayList(lineChars);

                    uniIndex = adjustedString.lastIndexOf(')');

                    if(uniIndex < adjustedString.length()-1)
                        while(lineChars.get(uniIndex).equals(' ')) lineChars.remove(uniIndex+1);

                    for(;lineChars.get(uniIndex-1).equals(' '); uniIndex--) lineChars.remove(uniIndex-1);
                }
                //nbt check
                else if(adjustedString.matches("if\\([a-zA-Z0-9]+\\s*=\\s*§[a-z_]+§\\s*(\\[(\\s*[a-zA-Z0-9]+\\s*=\\s*[a-zA-Z0-9]+\\s*,)*\\s*[a-zA-Z0-9]+\\s*=\\s*[a-zA-Z0-9]+])?\\s*(\\{[\\s\\S]+})?\\)(\\s*\\{)?"))
                {
                    for(int i = adjustedString.indexOf('{'); i >= 0; i--)
                    {
                        char character = lineChars.get(i);
                        if(character == '§' || character == ',' || character == '=')
                        {
                            while(lineChars.get(i+1).equals(' ')) lineChars.remove(i+1);
                            for(; lineChars.get(i-1).equals(' '); i--) lineChars.remove(i - 1);
                        }
                    }
                }

                adjustedString = stringFromArrayList(lineChars);

                if((uniIndex = adjustedString.lastIndexOf('{')) == adjustedString.length()-1)
                    for(;lineChars.get(uniIndex-1).equals(' '); uniIndex--)
                        lineChars.remove(uniIndex-1);
            }
            else if(line.startsWith("unless"))
            {
                while(lineChars.get(6).equals(' ')) lineChars.remove(6);
                while(lineChars.get(7).equals(' ')) lineChars.remove(7);

                adjustedString = stringFromArrayList(lineChars);
                boolean lengthIs2 = false;

                //variable check
                if(adjustedString.matches("unless\\([a-zA-Z0-9]+\\s*(<|<=|=|>=|>)\\s*[a-zA-Z0-9]+\\s*\\)(\\s*\\{)?"))
                {
                    for(String char2Comp : char2CompArray)
                    {
                        uniIndex = adjustedString.indexOf(char2Comp);
                        if(uniIndex != -1)
                        {
                            lengthIs2 = true;
                            break;
                        }
                    }

                    if(uniIndex == -1)
                    {
                        for(String char1Comp : char1CompArray)
                        {
                            uniIndex = adjustedString.indexOf(char1Comp);
                            if(uniIndex != -1) break;
                        }
                    }

                    if(lengthIs2) while(lineChars.get(uniIndex+2).equals(' ')) lineChars.remove(uniIndex+2);
                    else while(lineChars.get(uniIndex+1).equals(' ')) lineChars.remove(uniIndex+1);
                    for(;lineChars.get(uniIndex-1).equals(' ');uniIndex--) lineChars.remove(uniIndex-1);

                    adjustedString = stringFromArrayList(lineChars);

                    uniIndex = adjustedString.lastIndexOf(')');

                    if(uniIndex < adjustedString.length()-1)
                        while(lineChars.get(uniIndex).equals(' ')) lineChars.remove(uniIndex+1);

                    for(;lineChars.get(uniIndex-1).equals(' '); uniIndex--) lineChars.remove(uniIndex-1);
                }
                //nbt check
                else if(adjustedString.matches("unless\\([a-zA-Z0-9]+\\s*=\\s*§[a-z_]+§\\s*(\\[(\\s*[a-zA-Z0-9]+\\s*=\\s*[a-zA-Z0-9]+\\s*,)*\\s*[a-zA-Z0-9]+\\s*=\\s*[a-zA-Z0-9]+])?\\s*(\\{[\\s\\S]+})?\\)(\\s*\\{)?"))
                {
                    for(int i = adjustedString.indexOf('{'); i >= 0; i--)
                    {
                        char character = lineChars.get(i);
                        if(character == '§' || character == ',' || character == '=')
                        {
                            while(lineChars.get(i+1).equals(' ')) lineChars.remove(i+1);
                            for(; lineChars.get(i-1).equals(' '); i--) lineChars.remove(i - 1);
                        }
                    }
                }

                adjustedString = stringFromArrayList(lineChars);

                if((uniIndex = adjustedString.lastIndexOf('{')) == adjustedString.length()-1)
                    for(;lineChars.get(uniIndex-1).equals(' '); uniIndex--)
                        lineChars.remove(uniIndex-1);
            }
            else if(line.startsWith("var"))
            {
                while(lineChars.get(3).equals(' ')) lineChars.remove(3);
                while(lineChars.get(4).equals(' ')) lineChars.remove(4);

                adjustedString = stringFromArrayList(lineChars);
                String usedOperand = "=";
                int index = 1;

                for (String operand : operands)
                {
                    if(adjustedString.contains(operand))
                    {
                        usedOperand = operand;
                        index = adjustedString.indexOf(operand);
                        break;
                    }
                }

                int opLen = usedOperand.length();

                for(; lineChars.get(index-1).equals(' '); index--) lineChars.remove(index-1);
                while(lineChars.get(index+opLen).equals(' ')) lineChars.remove(index+opLen);
            }
            else if(line.startsWith("fnc"))
            {
                while(lineChars.get(3).equals(' ')) lineChars.remove(3);
                while(lineChars.get(4).equals(' ')) lineChars.remove(4);

                adjustedString = stringFromArrayList(lineChars);

                if((uniIndex = adjustedString.indexOf('?')) > -1)
                {
                    while(lineChars.get(uniIndex+1).equals(' ')) lineChars.remove(uniIndex+1);
                    for(; lineChars.get(uniIndex-1).equals(' '); uniIndex--) lineChars.remove(uniIndex-1);
                }
                else
                {
                    uniIndex = adjustedString.indexOf("->");
                    while(lineChars.get(uniIndex+2).equals(' ')) lineChars.remove(uniIndex+2);
                    for(; lineChars.get(uniIndex-1).equals(' '); uniIndex--) lineChars.remove(uniIndex-1);
                }
            }
            else if(line.startsWith("blk"))
            {
                while(lineChars.get(3).equals(' ')) lineChars.remove(3);
                while(lineChars.get(4).equals(' ')) lineChars.remove(4);

                adjustedString = stringFromArrayList(lineChars);

                //initialize block variable
                if(adjustedString.matches("blk:[a-zA-Z0-9]+\\s*=\\s*-?\\d+\\s*,\\s*-?\\d+\\s*,\\s*-?\\d+"))
                {
                    int[] commas = Util.getAllIndicesOf(adjustedString, ",");

                    for(int i = commas.length-1; i >= 0; i--)
                    {
                        uniIndex = commas[i];

                        while(lineChars.get(uniIndex+1).equals(' ')) lineChars.remove(uniIndex+1);
                        for(;lineChars.get(uniIndex-1).equals(' '); uniIndex--) lineChars.remove(uniIndex-1);
                    }

                    int assign = adjustedString.indexOf('=');

                    while(lineChars.get(assign+1).equals(' ')) lineChars.remove(assign+1);
                    for(;lineChars.get(assign-1).equals(' '); assign--) lineChars.remove(assign-1);
                }
                //set the type and state
                else if(adjustedString.matches("blk:[a-zA-Z0-9]+\\s*\\.\\s*type\\s*=\\s*§[a-z_]+§\\s*(\\[\\s*([a-z_0-9]+\\s*=\\s*[a-zA-Z0-9_]+\\s*,\\s*)*[a-z_0-9]+\\s*=\\s*[a-zA-Z0-9_]+\\s*])?(\\s*\\{[\\s\\S]+})?"))
                {
                    ArrayList<Integer> allAssigns = new ArrayList<>(Arrays.asList(ArrayUtils.toObject(Util.getAllIndicesOf(adjustedString, '='))));
                    final int firstCurly = adjustedString.indexOf('{');
                    allAssigns.removeIf((i) -> i > firstCurly);

                    for(int i = allAssigns.size()-1; i >= 0; i--)
                    {
                        int index = allAssigns.get(i);
                        while(lineChars.get(index+1).equals(' ')) lineChars.remove(index+1);
                        for(;lineChars.get(index-1).equals(' '); index--) lineChars.remove(index-1);
                    }

                    adjustedString = stringFromArrayList(lineChars);

                    int typeStart = adjustedString.indexOf('§');
                    int typeEnd = adjustedString.indexOf('§', typeStart+1);

                    while(lineChars.get(typeEnd+1).equals(' ')) lineChars.remove(typeEnd+1);
                    for(;lineChars.get(typeStart-1).equals(' '); typeStart--) lineChars.remove(typeStart);

                    adjustedString = stringFromArrayList(lineChars);

                    uniIndex = adjustedString.indexOf('.');

                    while(lineChars.get(uniIndex+1).equals(' ')) lineChars.remove(uniIndex+1);
                    for(;lineChars.get(uniIndex-1).equals(' '); uniIndex--) lineChars.remove(uniIndex-1);

                    adjustedString = stringFromArrayList(lineChars);

                    uniIndex = adjustedString.indexOf(']');

                    while(lineChars.get(uniIndex+1).equals(' ')) lineChars.remove(uniIndex+1);
                    for(;lineChars.get(uniIndex-1).equals(' '); uniIndex--) lineChars.remove(uniIndex-1);

                    uniIndex = adjustedString.indexOf('[');

                    while(lineChars.get(uniIndex+1).equals(' ')) lineChars.remove(uniIndex+1);
                    for(;lineChars.get(uniIndex-1).equals(' '); uniIndex--) lineChars.remove(uniIndex-1);

                    adjustedString = stringFromArrayList(lineChars);

                    ArrayList<Integer> allCommas = new ArrayList<>(Arrays.asList(ArrayUtils.toObject(Util.getAllIndicesOf(adjustedString, '='))));
                    final int firstCurly2 = adjustedString.indexOf('{');
                    allCommas.removeIf((i) -> i > firstCurly2);

                    for(int i = allCommas.size()-1; i >= 0; i--)
                    {
                        int index = allCommas.get(i);
                        while(lineChars.get(index+1).equals(' ')) lineChars.remove(index+1);
                        for(;lineChars.get(index-1).equals(' '); index--) lineChars.remove(index-1);
                    }

                    adjustedString = stringFromArrayList(lineChars);
                    int openNBT = adjustedString.indexOf('{');

                    String beforeNBT = adjustedString.substring(0, openNBT);
                    String theNBT = adjustedString.substring(openNBT);
                    ArrayList<Character> nbtChars = arrayListFromString(theNBT);

                    ArrayList<Integer> quotes = new ArrayList<>();

                    for(int i = 1; i < nbtChars.size(); i++)
                        if (nbtChars.get(i).equals('"') && !nbtChars.get(i - 1).equals('\\'))
                            quotes.add(i);

                    boolean inquote = false;

                    for(int i = nbtChars.size()-2; i >= 0; i--)
                    {
                        if(quotes.contains(i))
                        {
                            inquote = !inquote;
                            continue;
                        }

                        if(inquote) continue;

                        if(ArrayUtils.contains(jsonStrings, nbtChars.get(i).toString()))
                        {
                            while(nbtChars.size() != i+1 && nbtChars.get(i+1).equals(' ')) nbtChars.remove(i+1);
                            if(i > 0)
                                for(;nbtChars.get(i-1).equals(' '); i--) nbtChars.remove(i-1);

                            if(i < 0) break;
                        }
                    }

                    uniIndex = nbtChars.lastIndexOf('}');

                    for(;nbtChars.get(uniIndex-1).equals(' '); uniIndex--) nbtChars.remove(uniIndex-1);

                    theNBT = stringFromArrayList(nbtChars);
                    adjustedString = beforeNBT.concat(theNBT);
                    lineChars = arrayListFromString(adjustedString);
                }
                //append
                else if(adjustedString.matches("blk:[a-zA-Z0-9]+(\\s*\\.\\s*[a-zA-Z]+)+\\s*->\\s*((-?\\d*?\\.\\d+|-?\\d+[sfdb]?)|(\"[\\s\\S]*\")|\\{[\\s\\S]*})"))
                {
                    int assign = adjustedString.indexOf('>');
                    while(lineChars.get(assign+1).equals(' ')) lineChars.remove(assign+1);
                    assign = adjustedString.indexOf('-');
                    for(;lineChars.get(assign-1).equals(' '); assign--) lineChars.remove(assign-1);

                    adjustedString = stringFromArrayList(lineChars);
                    int newAssign = adjustedString.indexOf('-');
                    ArrayList<Integer> dotIndicesList = new ArrayList<>(Arrays.asList(ArrayUtils.toObject(Util.getAllIndicesOf(adjustedString, "."))));
                    dotIndicesList.removeIf((i) -> i >= newAssign);
                    Collections.reverse(dotIndicesList);
                    int[] dotIndices = ArrayUtils.toPrimitive(dotIndicesList.toArray(new Integer[0]));

                    for(int dotIndex : dotIndices)
                    {
                        while(lineChars.get(dotIndex+1).equals(' ')) lineChars.remove(dotIndex+1);
                        for(;lineChars.get(dotIndex-1).equals(' '); dotIndex--) lineChars.remove(dotIndex-1);
                    }

                    adjustedString = stringFromArrayList(lineChars);

                    int squareIndex = adjustedString.indexOf('[');

                    if(squareIndex != -1)
                    {
                        while(lineChars.get(squareIndex+1).equals(' ')) lineChars.remove(squareIndex+1);
                        adjustedString = stringFromArrayList(lineChars);
                        squareIndex = adjustedString.indexOf(']');
                        for(;lineChars.get(squareIndex-1).equals(' '); squareIndex--) lineChars.remove(squareIndex-1);
                    }
                }
                //prepend
                else if(adjustedString.matches("blk:[a-zA-Z0-9]+(\\s*\\.\\s*[a-zA-Z]+)+\\s*<-\\s*((-?(\\d*?\\.\\d+)|-?(\\d+)[sfdb]?)|(\"[\\s\\S]*\")|\\{[\\s\\S]*})"))
                {
                    int assign = adjustedString.indexOf('-');
                    while(lineChars.get(assign+1).equals(' ')) lineChars.remove(assign+1);
                    assign = adjustedString.indexOf('<');
                    for(;lineChars.get(assign-1).equals(' '); assign--) lineChars.remove(assign-1);

                    adjustedString = stringFromArrayList(lineChars);
                    int newAssign = adjustedString.indexOf('<');
                    ArrayList<Integer> dotIndicesList = new ArrayList<>(Arrays.asList(ArrayUtils.toObject(Util.getAllIndicesOf(adjustedString, "."))));
                    dotIndicesList.removeIf((i) -> i >= newAssign);
                    Collections.reverse(dotIndicesList);
                    int[] dotIndices = ArrayUtils.toPrimitive(dotIndicesList.toArray(new Integer[0]));

                    for(int dotIndex : dotIndices)
                    {
                        while(lineChars.get(dotIndex+1).equals(' ')) lineChars.remove(dotIndex+1);
                        for(;lineChars.get(dotIndex-1).equals(' '); dotIndex--) lineChars.remove(dotIndex-1);
                    }

                    adjustedString = stringFromArrayList(lineChars);

                    int squareIndex = adjustedString.indexOf('[');

                    if(squareIndex != -1)
                    {
                        while(lineChars.get(squareIndex+1).equals(' ')) lineChars.remove(squareIndex+1);
                        adjustedString = stringFromArrayList(lineChars);
                        squareIndex = adjustedString.indexOf(']');
                        for(;lineChars.get(squareIndex-1).equals(' '); squareIndex--) lineChars.remove(squareIndex-1);
                    }
                }
                //insert into array
                else if(adjustedString.matches("blk:[a-zA-Z0-9]+(\\s*\\.\\s*[a-zA-Z]+)+\\s*\\[\\d+]\\s*\\+=\\s*((-?(\\d*?\\.\\d+)|-?(\\d+)[sfdb]?)|(\"[\\s\\S]*\")|\\{[\\s\\S]*})"))
                {
                    int assign = adjustedString.indexOf('=');
                    while(lineChars.get(assign+1).equals(' ')) lineChars.remove(assign+1);
                    assign = adjustedString.indexOf('+');
                    for(;lineChars.get(assign-1).equals(' '); assign--) lineChars.remove(assign-1);

                    adjustedString = stringFromArrayList(lineChars);
                    int newAssign = adjustedString.indexOf('+');
                    ArrayList<Integer> dotIndicesList = new ArrayList<>(Arrays.asList(ArrayUtils.toObject(Util.getAllIndicesOf(adjustedString, "."))));
                    dotIndicesList.removeIf((i) -> i >= newAssign);
                    Collections.reverse(dotIndicesList);
                    int[] dotIndices = ArrayUtils.toPrimitive(dotIndicesList.toArray(new Integer[0]));

                    for(int dotIndex : dotIndices)
                    {
                        while(lineChars.get(dotIndex+1).equals(' ')) lineChars.remove(dotIndex+1);
                        for(;lineChars.get(dotIndex-1).equals(' '); dotIndex--) lineChars.remove(dotIndex-1);
                    }

                    adjustedString = stringFromArrayList(lineChars);

                    int squareIndex = adjustedString.indexOf('[');

                    if(squareIndex != -1)
                    {
                        while(lineChars.get(squareIndex+1).equals(' ')) lineChars.remove(squareIndex+1);
                        adjustedString = stringFromArrayList(lineChars);
                        squareIndex = adjustedString.indexOf(']');
                        for(;lineChars.get(squareIndex-1).equals(' '); squareIndex--) lineChars.remove(squareIndex-1);
                    }
                }
            }
            else throw new UnknownStatementException(line, lineNum, function.getPath());

            return stringFromArrayList(lineChars);
        }
        catch(Throwable t)
        {
            if(!(t instanceof CompilationException))
                throw new WrappedCompilationException(lineNum, t, function.getPath());
            else
                throw t;
        }
    }

    private static ArrayList<String> simplify(ArrayList<String> text)
    {
        for(int i = 0; i < text.size(); i++)
        {
            String line = text.get(i);

            if((line.startsWith("if") || line.startsWith("unless")) && line.endsWith("{"))
            {
                line = line.substring(0, line.length()-1);
                text.set(i, line);
                text.add(i+1, "{");
            }

            if(line.matches("var:([a-zA-Z0-9]+)=\\1"))
            {
                text.remove(i);
                continue;
            }

            if(line.matches("if\\(([a-zA-Z0-9]+)[<>]\\1\\)") || line.matches("unless\\(([a-zA-Z0-9]+)(==|<=|>=)\\1\\)"))
            {
                int blocks = 1;

                text.remove(i);
                text.remove(i);

                while(blocks != 0)
                {
                    String removed = text.remove(i);

                    if(removed.equals("{"))
                        blocks++;
                    if(removed.equals("}"))
                        blocks--;
                }

                if(i == text.size())
                    return text;

                i = -1;
            }

            if(line.matches("if\\(([a-zA-Z0-9]+)(==|>=|<=)\\1\\)") || line.matches("unless\\(([a-zA-Z0-9]+)[<>]\\1\\)"))
            {
                int blocks = 1;

                text.remove(i);
                text.remove(i);
                int j;

                for(j = i; blocks != 0; j++)
                {
                    String s = text.get(j);

                    if(s.equals("{"))
                        blocks++;
                    if(s.equals("}"))
                        blocks--;
                }

                text.remove(j-1);

                if(j == text.size())
                    return text;

                i = -1;
            }
        }

        return text;
    }

    private static String stringFromArrayList(ArrayList<Character> chars)
    {
        return new String(ArrayUtils.toPrimitive(chars.toArray(new Character[0])));
    }

    private static ArrayList<Character> arrayListFromString(String string)
    {
        return new ArrayList<>(Arrays.asList(ArrayUtils.toObject(string.toCharArray())));
    }
}


<|start_filename|>src/io/github/keheck/util/Log.java<|end_filename|>
package io.github.keheck.util;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.*;

public class Log
{
    private static final Logger NORMAL_LOGGER = Logger.getLogger("DatPackLog");
    private static final Logger ERROR_LOGGER = Logger.getLogger("DatPackErr");

    private static final File NORMAL_FILE = new File(Directories.rootDir, "runtime.txt");
    private static final File ERROR_FILE = new File(Directories.rootDir, "error.txt");

    public static void init()
    {
        createRuntimeLog();
        createErrorLog();

        NORMAL_LOGGER.setUseParentHandlers(false);
        NORMAL_LOGGER.setLevel(Level.ALL);

        ERROR_LOGGER.setUseParentHandlers(false);
        ERROR_LOGGER.setLevel(Level.SEVERE);
    }

    private static void createRuntimeLog()
    {
        try
        {
            NORMAL_FILE.createNewFile();
            FileHandler handler = new FileHandler(NORMAL_FILE.getPath());
            handler.setFormatter(new LogFormatter());
            NORMAL_LOGGER.addHandler(handler);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    private static void createErrorLog()
    {
        try
        {
            ERROR_FILE.createNewFile();
            FileHandler handler = new FileHandler(ERROR_FILE.getPath());
            handler.setFormatter(new LogFormatter());
            ERROR_LOGGER.addHandler(handler);
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }

    public static void convertLogs()
    {

    }

    public static void i(String msg) { NORMAL_LOGGER.log(Level.INFO, msg); }

    public static void f1(String msg) { NORMAL_LOGGER.log(Level.FINE, msg); }

    public static void f2(String msg) { NORMAL_LOGGER.log(Level.FINER, msg); }

    public static void f3(String msg) { NORMAL_LOGGER.log(Level.FINEST, msg); }

    public static void e(String msg) { NORMAL_LOGGER.log(Level.SEVERE, msg); }

    public static void e(String msg, Throwable t) { ERROR_LOGGER.log(Level.SEVERE, msg, t); }

    public static void w(String msg) { NORMAL_LOGGER.log(Level.WARNING, msg); }

    private static class LogFormatter extends Formatter
    {
        String newLine = System.lineSeparator();

        @Override
        public String format(LogRecord record)
        {
            StringBuilder builder = new StringBuilder(256);

            if(record.getMessage().equals("\n"))
                return newLine;

            if(record.getMessage().charAt(0) == '\n')
            {
                builder.append(newLine);
                record.setMessage(record.getMessage().substring(1));
            }

            Level level = record.getLevel();

            String prefix = " [---]: ";

            if(level == Level.FINE)
                prefix = " [TRACING]: ";
            if(level == Level.FINER)
                prefix = " [DETAILED]: ";
            if(level == Level.FINEST)
                prefix = " [HIGH DETAIL]: ";
            if(level == Level.INFO)
                prefix = " [INFO]: ";
            if(level == Level.SEVERE)
                prefix = " [ERROR]: ";
            if(level == Level.WARNING)
                prefix = " [WARN]: ";

            SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
            String time = dateFormat.format(new Date());

            record.setMessage(record.getMessage().replaceAll("\n", newLine + time + prefix));

            builder.append(time).append(prefix).append(formatMessage(record)).append(newLine);

            Throwable throwable = record.getThrown();

            if(throwable != null)
            {
                builder.append("at ").
                        append(record.getSourceClassName()).
                        append(":").append(record.getSourceMethodName()).
                        append(newLine);

                StringWriter writer = new StringWriter();
                throwable.printStackTrace(new PrintWriter(writer, true));
                builder.append(writer.toString());
                builder.append(newLine);
            }

            if(level == Level.WARNING || level == Level.SEVERE)
                System.err.print(builder.toString());
            else
                System.out.print(builder.toString());

            return builder.toString();
        }
    }
}


<|start_filename|>src/io/github/keheck/project/compiling/NumberGroup.java<|end_filename|>
package io.github.keheck.project.compiling;

class NumberGroup
{
    private int[] group;

    NumberGroup(int... nums)
    {
        group = nums;
    }

    int[] getGroup() { return group; }

    int get(int i) { return group[i]; }
}


<|start_filename|>src/io/github/keheck/tree/NavTreeSelectionListener.java<|end_filename|>
package io.github.keheck.tree;

import io.github.keheck.Main;
import io.github.keheck.window.EditPanel;

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;

public class NavTreeSelectionListener implements TreeSelectionListener
{
    private static AbstractNavTreeNode lastFileSelected;

    @Override
    public void valueChanged(TreeSelectionEvent e)
    {
        //TEMPORÄRE LÖSUNG
        AbstractNavTreeNode node = (AbstractNavTreeNode)Main.navTree.getLastSelectedPathComponent();

        if(lastFileSelected instanceof NavTreeFile)
        {
            String lineBreak = "\n";
            String text = Main.editArea.getText();
            text = String.join("    ", text.split("\t"));
            String[] lines = text.split(lineBreak);
            ArrayList<String> linesList = new ArrayList<>(Arrays.asList(lines));
            Main.texts.put((NavTreeFile)lastFileSelected, linesList);
        }

        if(node != null && node.getType().isLeaf())
        {
            ArrayList<String> lines = Main.texts.get(node);
            String text = String.join("\n", lines.toArray(new String[0]));
            Main.editArea = new JTextArea(text);
            Font mono = new Font("Monospaced", Font.PLAIN, 12);
            Main.editArea.setFont(mono);
            Main.editArea.setTabSize(5);
            EditPanel.setupActions(Main.editArea);
            Main.editContainer.setViewportView(Main.editArea);
            lastFileSelected = node;
        }
        else
        {
            Main.editArea = new JTextArea("");
            Main.editArea.setEnabled(false);
            Main.editContainer.setViewportView(Main.editArea);
            lastFileSelected = null;
        }
    }
}


<|start_filename|>src/io/github/keheck/project/exceptions/UnknownStatementException.java<|end_filename|>
package io.github.keheck.project.exceptions;

public class UnknownStatementException extends SyntaxException
{
    public UnknownStatementException(String syntaxError, int lineNum, String function)
    {
        super("unknown or wrong syntax: \"" + syntaxError + "\"", lineNum, function);
    }
}


<|start_filename|>src/io/github/keheck/tree/AbstractNavTreeNode.java<|end_filename|>
package io.github.keheck.tree;

import io.github.keheck.window.Icons;

import javax.swing.*;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;

public abstract class AbstractNavTreeNode implements MutableTreeNode, Cloneable, Serializable
{
    private NodeType type;
    private AbstractNavTreeNode parent;
    private String name;

    AbstractNavTreeNode(NodeType type, String name)
    {
        this.type = type;
        this.name = name;
    }

    public NodeType getType() { return type; }

    public String getName() { return name; }

    void setParent(AbstractNavTreeNode parent) { this.parent = parent; }

    ImageIcon getIcon() { return type.icon; }

    @Override
    public TreeNode getChildAt(int childIndex) { return null; }

    @Override
    public TreeNode getParent() { return parent; }

    @Override
    public boolean getAllowsChildren() { return !type.isLeaf; }

    @Override
    public Enumeration<TreeNode> children() { return Collections.emptyEnumeration(); }

    @Override
    public int getChildCount() { return 0; }

    @Override
    public int getIndex(TreeNode node) { return 0; }

    @Override
    public String toString() { return name + type.getExtension(); }

    public String getPath()
    {
        AbstractNavTreeNode treeNode = this;

        ArrayList<AbstractNavTreeNode> nodes = new ArrayList<>();

        nodes.add(treeNode);
        treeNode = (AbstractNavTreeNode)treeNode.getParent();

        while (treeNode != null)
        {
            nodes.add(0, treeNode);
            treeNode = (AbstractNavTreeNode)treeNode.getParent();
        }

        String[] path = new String[nodes.size()];

        for(int i = 0; i < path.length; i++)
        {
            path[i] = nodes.get(i).toString();
        }

        return String.join("/", path);
    }

    public enum NodeType
    {
        JSON(Icons.FILE_JSON, true, ".json"),
        NAMESPACE(Icons.FILE_NAMESPACE, false),
        MCFUNCTION(Icons.FILE_MCFUNC, true, ".mcfunction"),
        META(Icons.FILE_METADATA, true, ".mcmeta"),
        FOLDER(Icons.FILE_FOLDER, false),
        UNKNOWN(Icons.FILE_UNKNOWN, true);

        private ImageIcon icon;
        private boolean isLeaf;
        private String extension;

        NodeType(ImageIcon icon, boolean isLeaf) { this(icon, isLeaf, ""); }

        NodeType(ImageIcon icon, boolean isLeaf, String extension)
        {
            this.icon = icon;
            this.isLeaf = isLeaf;
            this.extension = extension;
        }

        public static NodeType getNodeType(String extension)
        {
            switch(extension)
            {
                default:
                    return UNKNOWN;
                case ".json":
                    return JSON;
                case ".mcfunction":
                    return MCFUNCTION;
                case ".mcmeta":
                    return META;
            }
        }

        public boolean isLeaf() { return isLeaf; }

        public ImageIcon getIcon() { return icon; }

        public String getExtension() { return extension; }
    }
}


<|start_filename|>src/io/github/keheck/window/dialogs/DialogDeleteFile.java<|end_filename|>
package io.github.keheck.window.dialogs;

import io.github.keheck.Main;
import io.github.keheck.Tasks;
import io.github.keheck.tree.AbstractNavTreeNode;
import io.github.keheck.util.Log;

import javax.swing.*;
import java.awt.*;

public class DialogDeleteFile extends JDialog
{
    private JButton approve;
    private JButton cancel;
    private AbstractNavTreeNode node;

    public DialogDeleteFile(AbstractNavTreeNode node)
    {
        super(Main.frame, "Delete");
        Log.i("Opening dialog DialogDeleteFile");
        JLabel delete = new JLabel("You are about to delete: ");
        JLabel file = new JLabel(node.toString());
        JLabel question = new JLabel("Are you sure? (Children will be deleted as well)");

        approve = new JButton("Ok");
        cancel = new JButton("Cancel");
        this.node = node;

        file.setForeground(new Color(0xFF0000));

        JPanel host = new JPanel();
        add(host);

        GroupLayout layout = new GroupLayout(host);
        host.setLayout(layout);

        layout.setAutoCreateContainerGaps(true);
        layout.setAutoCreateGaps(true);

        layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                .addGroup(layout.createSequentialGroup()
                        .addComponent(delete)
                        .addComponent(file))
                .addComponent(question)
                .addGroup(layout.createSequentialGroup()
                        .addComponent(approve)
                        .addComponent(cancel))
        );

        layout.setVerticalGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                        .addComponent(delete)
                        .addComponent(file))
                .addComponent(question)
                .addGroup(layout.createParallelGroup()
                        .addComponent(approve)
                        .addComponent(cancel))
        );

        setMinimumSize(new Dimension(400, 200));
        setResizable(false);
        setLocationRelativeTo(null);
        setupActions();
        setVisible(true);
    }

    private void setupActions()
    {
        approve.addActionListener(e -> Tasks.delete(node, this));
        cancel.addActionListener(e -> dispose());
    }
}


<|start_filename|>src/io/github/keheck/window/NavTree.java<|end_filename|>
package io.github.keheck.window;

import io.github.keheck.Main;
import io.github.keheck.tree.*;
import org.jdesktop.swingx.renderer.DefaultTreeRenderer;

import javax.swing.*;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreeSelectionModel;

import java.util.ArrayList;
import java.util.Enumeration;

import static io.github.keheck.tree.AbstractNavTreeNode.NodeType.*;

public class NavTree
{
    public static NavTreeFile meta;

    public static void cunstructTree(String projName, String nameSpace)
    {
        if("".equals(nameSpace) || nameSpace == null) nameSpace = projName;
        NavTreeFolder root = new NavTreeFolder(FOLDER, projName);
        JTree tree = Main.navTree;
        tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        tree.setModel(new DefaultTreeModel(root));
        tree.getModel().addTreeModelListener(new NavTreeModelListener());
        tree.addTreeSelectionListener(new NavTreeSelectionListener());
        tree.setCellRenderer(new DefaultTreeRenderer(new NodeIconProvider()));
        DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
            NavTreeFolder minecraft = new NavTreeFolder(NAMESPACE, "minecraft");
            model.insertNodeInto(minecraft, root, 0);
                NavTreeFolder advancementsMC = new NavTreeFolder(FOLDER, "advancements");
                model.insertNodeInto(advancementsMC, minecraft, 0);

                NavTreeFolder loot_tablesMC = new NavTreeFolder(FOLDER, "loot_tables");
                model.insertNodeInto(loot_tablesMC, minecraft, 1);

                NavTreeFolder recipiesMC = new NavTreeFolder(FOLDER, "recipies");
                model.insertNodeInto(recipiesMC, minecraft, 2);

                NavTreeFolder tagsMC = new NavTreeFolder(FOLDER, "tags");
                model.insertNodeInto(tagsMC, minecraft, 3);

            NavTreeFolder name = new NavTreeFolder(NAMESPACE, nameSpace);
            model.insertNodeInto(name, root, 1);
                NavTreeFolder advancements = new NavTreeFolder(FOLDER, "advancements");
                model.insertNodeInto(advancements, name, 0);

                NavTreeFolder functions = new NavTreeFolder(FOLDER, "functions");
                model.insertNodeInto(functions, name, 1);

                NavTreeFolder loot_tables = new NavTreeFolder(FOLDER, "loot_tables");
                model.insertNodeInto(loot_tables, name, 2);

                NavTreeFolder recipies = new NavTreeFolder(FOLDER, "recipies");
                model.insertNodeInto(recipies, name, 3);

                NavTreeFolder structures = new NavTreeFolder(FOLDER, "structures (not supported)");
                model.insertNodeInto(structures, name, 4);

                NavTreeFolder tags = new NavTreeFolder(FOLDER, "tags");
                model.insertNodeInto(tags, name, 5);

            meta = new NavTreeFile(META, "pack");
            model.insertNodeInto(meta, root, 2);

        ArrayList<String> metaText = new ArrayList<>();

        metaText.add("{");
        metaText.add("    \"pack\":");
        metaText.add("    {");
        metaText.add("        \"description\": \"Description here\",");
        metaText.add("        \"pack_format\": 1");
        metaText.add("    }");
        metaText.add("}");

        Main.texts.put(meta, metaText);
        Main.project = projName;
    }

    public static int getChildIndex(Enumeration<TreeNode> others, AbstractNavTreeNode toInsert)
    {
        int index = 0;

        while(others.hasMoreElements())
        {
            AbstractNavTreeNode node = (AbstractNavTreeNode)others.nextElement();
            boolean insertIsLeaf = toInsert.getType().isLeaf();
            boolean nodeIsLeaf = node.getType().isLeaf();

            if(insertIsLeaf)
            {
                if(!nodeIsLeaf)
                    index++;
                else if(node.getName().compareToIgnoreCase(toInsert.getName()) < 0)
                    index++;
                else
                    return index;
            }
            else
            {
                if(nodeIsLeaf)
                    return index;
                else if(node.getName().compareToIgnoreCase(toInsert.getName()) < 0)
                    index++;
                else
                    return index;
            }
        }

        return index;
    }
}


<|start_filename|>src/io/github/keheck/window/dialogs/DialogCompileProject.java<|end_filename|>
package io.github.keheck.window.dialogs;

import io.github.keheck.Main;
import io.github.keheck.Tasks;
import io.github.keheck.project.saveandload.Load;
import io.github.keheck.util.Directories;

import javax.swing.*;
import java.awt.*;

public class DialogCompileProject extends JDialog
{
    private JFileChooser fileChooser;

    public DialogCompileProject()
    {
        super(Main.frame, "Compile project...");
        fileChooser = new JFileChooser(Directories.mcDir);
        fileChooser.setFileFilter(new DialogComileProjFilter());
        fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        JPanel panel = new JPanel();
        panel.add(fileChooser);
        add(panel);

        setLocationRelativeTo(null);
        setMinimumSize(new Dimension(600, 400));
        setResizable(false);
        setupActions();
        setVisible(true);
    }

    private void setupActions()
    {
        fileChooser.addActionListener(e -> {
            if(e.getActionCommand().toLowerCase().contains("approve"))
                //System.out.println(fileChooser.getSelectedFile());
                Tasks.compile(fileChooser.getSelectedFile());
            setVisible(false);
        });
    }
}


<|start_filename|>src/io/github/keheck/window/MainMenu.java<|end_filename|>
package io.github.keheck.window;

import io.github.keheck.Main;
import io.github.keheck.Tasks;
import io.github.keheck.project.saveandload.Load;
import io.github.keheck.project.saveandload.Save;
import io.github.keheck.window.dialogs.*;
import io.github.keheck.tree.AbstractNavTreeNode;

import javax.swing.*;

public class MainMenu
{
    private static JMenuBar menuBar;

    private static JMenu file;
    private static JMenuItem newProject;
    private static JMenuItem exportProject;
    private static JMenuItem saveProject;
    private static JMenuItem loadProject;

    private static JMenu newFile;
    private static JMenuItem newFunc;
    private static JMenuItem newJson;
    private static JMenuItem newNamespace;
    private static JMenuItem newFolder;
    private static JMenuItem deleteFile;

    public static JMenu settings;
    public static JCheckBoxMenuItem commandsOnly;
    public static JCheckBoxMenuItem keepComments;

    public static JMenuBar setupMenu()
    {
        JFrame frame = Main.frame;

        menuBar = new JMenuBar();

            file = new JMenu("File");

                newProject = new JMenuItem("New Project");
                newProject.setIcon(Icons.PROJECT_NEW);
            file.add(newProject);

                loadProject = new JMenuItem("Load Project");
                loadProject.setIcon(Icons.PROJECT_LOAD);
            file.add(loadProject);

                exportProject = new JMenuItem("Compile Project");
                exportProject.setIcon(Icons.PROJECT_EXPORT);
            file.add(exportProject);

                saveProject = new JMenuItem("Save Project");
                saveProject.setIcon(Icons.PROJECT_SAVE);
            file.add(saveProject);

            file.addSeparator();

                newFile = new JMenu("New");
                newFile.setIcon(Icons.FILE_NEWFILE);

                    newFunc = new JMenuItem("MCFunction-File");
                    newFunc.setIcon(Icons.FILE_MCFUNC);
                newFile.add(newFunc);

                    newJson = new JMenuItem("JSON-File");
                    newJson.setIcon(Icons.FILE_JSON);
                newFile.add(newJson);

                    newNamespace = new JMenuItem("Namespace");
                    newNamespace.setIcon(Icons.FILE_NAMESPACE);
                newFile.add(newNamespace);

                    newFolder = new JMenuItem("Folder");
                    newFolder.setIcon(Icons.FILE_FOLDER);
                newFile.add(newFolder);

                deleteFile = new JMenuItem("Delete");
                deleteFile.setIcon(Icons.FILE_REMOVEFILE);
            file.add(deleteFile);
            file.add(newFile);

            settings = new JMenu("Project Settings");
                commandsOnly = new JCheckBoxMenuItem("Use Minecraft commands only");
                settings.add(commandsOnly);

                keepComments = new JCheckBoxMenuItem("Keep comments after compilation");
                settings.add(keepComments);

        menuBar.add(file);
        menuBar.add(settings);

        frame.setJMenuBar(menuBar);

        setupActions();

        return menuBar;
    }

    public static void setUseComments(boolean value) { keepComments.setState(value); }

    public static void setUseVanilla(boolean value) { commandsOnly.setState(value); }

    private static void setupActions()
    {
        newProject.addActionListener(e -> new DialogNewProject());
        newFunc.addActionListener(e -> Tasks.initNewFile(AbstractNavTreeNode.NodeType.MCFUNCTION));
        newJson.addActionListener(e -> Tasks.initNewFile(AbstractNavTreeNode.NodeType.JSON));
        newNamespace.addActionListener(e -> new DialogNewNamespace());
        newFolder.addActionListener(e -> new DialogNewFolder((AbstractNavTreeNode)Main.navTree.getLastSelectedPathComponent()));
        deleteFile.addActionListener(e -> new DialogDeleteFile((AbstractNavTreeNode)Main.navTree.getLastSelectedPathComponent()));
        saveProject.addActionListener(e -> Save.save(Main.project));
        loadProject.addActionListener(e -> Load.initLoad());
        exportProject.addActionListener(e -> new DialogCompileProject());
    }
}


<|start_filename|>src/io/github/keheck/util/Validator.java<|end_filename|>
package io.github.keheck.util;

import io.github.keheck.tree.AbstractNavTreeNode.NodeType;

public class Validator
{
    public static boolean isValidString(String toTest, NodeType type)
    {
        return toTest.matches("[a-z0-9_]+");
    }

    public static boolean between(int min, int val, int max) { return min <= val && val <= max; }

    public static boolean isValidNamespace(String nameSpace)
    {
        switch(nameSpace.toLowerCase().trim())
        {
            case "if":
            case "for":
            case "while":
            case "else":
                return false;
            default:
                return true;
        }
    }
}


<|start_filename|>src/io/github/keheck/project/exceptions/CompilationException.java<|end_filename|>
package io.github.keheck.project.exceptions;

public class CompilationException extends RuntimeException
{
    private boolean isError;
    private static String message;

    CompilationException(int lineNum, String reason, String function, boolean isError)
    {
        message = this.toString() + ": " + reason + " at " + function + ":" + lineNum;
        this.isError = isError;
    }

    /**
     * When an exception is "heavy" it means it has
     * such a strong impact of the code that the compilation
     * cannot be continued on the function and the compiler will
     * interpret this as an error and continue with the next file
     */
    public boolean isError() { return isError; }

    @Override
    public String getMessage() { return message; }

    @Override
    public String toString() { return getClass().getSimpleName(); }
}


<|start_filename|>src/io/github/keheck/window/dialogs/DialogErrorNameInvalid.java<|end_filename|>
package io.github.keheck.window.dialogs;

import io.github.keheck.util.Log;

import javax.swing.*;
import java.awt.*;

public class DialogErrorNameInvalid extends JDialog
{
    private JButton ok;

    public DialogErrorNameInvalid(String fileName, JDialog owner)
    {
        super(owner, "ERROR");
        JPanel panel = new JPanel();
        this.add(panel);

        ok = new JButton("Ok");
        Log.i("Opening dialog DialogErrorNameInvalid");
        JLabel label = new JLabel("Invalid name! Only alphanumerical characters and the underscore");
        JPanel textWrapper = new JPanel();
        JLabel area = new JLabel(fileName);
        textWrapper.setBackground(new Color(255, 255, 255));
        area.setForeground(new Color(0xA40000));
        textWrapper.add(area);

        GroupLayout layout = new GroupLayout(panel);
        panel.setLayout(layout);

        layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                .addComponent(label)
                .addComponent(textWrapper, GroupLayout.Alignment.CENTER)
                .addComponent(ok)
        );

        layout.setVerticalGroup(layout.createSequentialGroup()
                .addComponent(label)
                .addGap(10)
                .addComponent(textWrapper, 25, 25, 25)
                .addComponent(ok)
        );

        setMinimumSize(new Dimension(350, 125));
        setResizable(false);
        setLocationRelativeTo(null);
        setupActions();
        setVisible(true);
    }

    private void setupActions() { ok.addActionListener(e -> dispose()); }
}


<|start_filename|>src/io/github/keheck/Tasks.java<|end_filename|>
package io.github.keheck;

import io.github.keheck.project.compiling.Compiler;
import io.github.keheck.util.Log;
import io.github.keheck.util.Validator;
import io.github.keheck.window.NavTree;
import io.github.keheck.window.dialogs.*;
import io.github.keheck.tree.AbstractNavTreeNode;
import io.github.keheck.tree.NavTreeFile;
import io.github.keheck.tree.NavTreeFolder;

import javax.swing.*;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;

import static io.github.keheck.tree.AbstractNavTreeNode.NodeType;

public class Tasks
{
    public static void onNewProject(String projName, String nameSpace, DialogNewProject project)
    {
        new Thread(() ->
        {
            String myProjName = projName;
            String myNamespace = nameSpace;

            if(myProjName == null) myProjName = "";
            if(myNamespace == null) myNamespace = "";

            if(!myProjName.matches("[a-z0-9_]+"))
            {
                new DialogErrorNameInvalid(myProjName, project);
                return;
            }

            if(!myNamespace.matches("[a-z0-9_]+"))
            {
                new DialogErrorNameInvalid(myNamespace, project);
                return;
            }

            NavTree.cunstructTree(projName, myNamespace);
            if(project != null)
                project.dispose();
        }, "Tree Constructor").start();
    }

    public static Thread constructDynamicDialog()
    {
        return new DialogUpdater();
    }

    public static void initNewFile(NodeType type)
    {
        AbstractNavTreeNode node = (AbstractNavTreeNode)Main.navTree.getLastSelectedPathComponent();
        NodeType parentType = node.getType();

        if(parentType != NodeType.FOLDER && parentType != NodeType.NAMESPACE)
            node = (AbstractNavTreeNode)node.getParent();

        new DialogNewFile(type, node);
    }

    public static void createNewFile(String fileName, NodeType fileType, AbstractNavTreeNode parent, DialogNewFile dialog)
    {
        Log.f1("Attempting to create new file");

        if(Validator.isValidString(fileName, fileType))
        {
            Enumeration<TreeNode> nodes = parent.children();

            while(nodes.hasMoreElements())
            {
                TreeNode node = nodes.nextElement();

                if(node instanceof AbstractNavTreeNode && ((AbstractNavTreeNode) node).getType() == fileType)
                {
                    if(((AbstractNavTreeNode)node).getName().equalsIgnoreCase(fileName) && ((AbstractNavTreeNode) node).getType() == fileType)
                    {
                        new DialogErrorNameSame(dialog);
                        return;
                    }
                }
            }

            NavTreeFile file = new NavTreeFile(fileType, fileName);
            ((DefaultTreeModel)Main.navTree.getModel()).insertNodeInto(file, parent, NavTree.getChildIndex(parent.children(), file));
            dialog.setVisible(false);
            Main.texts.put(file, new ArrayList<>());
        }
        else
        {
            new DialogErrorNameInvalid(fileName, dialog);
        }
    }

    public static void createNamespace(String folderName, JDialog dialog)
    {
        Log.f1("Attempting to create new namespace");

        if(Validator.isValidString(folderName, NodeType.NAMESPACE))
        {
            if(Validator.isValidNamespace(folderName))
            {
                AbstractNavTreeNode parent = (AbstractNavTreeNode)Main.navTree.getModel().getRoot();
                Enumeration<TreeNode> children = parent.children();

                while(children.hasMoreElements())
                {
                    AbstractNavTreeNode node = (AbstractNavTreeNode)children.nextElement();

                    if(node.getName().equalsIgnoreCase(folderName) && !node.getType().isLeaf())
                    {
                        new DialogErrorNameSame(dialog);
                        return;
                    }
                }

                NavTreeFolder child = new NavTreeFolder(NodeType.NAMESPACE, folderName);
                ((DefaultTreeModel)Main.navTree.getModel()).insertNodeInto(child, parent, NavTree.getChildIndex(parent.children(), child));

                dialog.setVisible(false);
            }
            else
            {
                new DialogErrorNamespaceReserved(folderName, dialog);
            }
        }
        else
        {
            new DialogErrorNameInvalid(folderName, dialog);
        }
    }

    public static void createFolder(String folderName, JDialog dialog, AbstractNavTreeNode parent)
    {
        Log.f1("Attempting to create new folder");

        if(Validator.isValidString(folderName, NodeType.FOLDER))
        {
            Enumeration<TreeNode> children = parent.children();

            while(children.hasMoreElements())
            {
                AbstractNavTreeNode node = (AbstractNavTreeNode)children.nextElement();

                if(node.getName().equalsIgnoreCase(folderName) && !node.getType().isLeaf())
                {
                    new DialogErrorNameSame(dialog);
                    return;
                }
            }

            NavTreeFolder child = new NavTreeFolder(NodeType.FOLDER, folderName);
            ((DefaultTreeModel)Main.navTree.getModel()).insertNodeInto(child, parent, NavTree.getChildIndex(parent.children(), child));

            dialog.setVisible(false);
        }
        else
        {
            new DialogErrorNameInvalid(folderName, dialog);
        }
    }

    public static void delete(AbstractNavTreeNode node, JDialog dialog)
    {
        ((DefaultTreeModel)Main.navTree.getModel()).removeNodeFromParent(node);
        dialog.dispose();
    }

    public static void compile(File destination)
    {
        new Thread(() -> Compiler.init(destination), "Compiler Thread").start();
    }

    public static class DialogUpdater extends Thread
    {
        private DialogDynamic dialog = new DialogDynamic();

        private DialogUpdater()
        {
            super("Dialog updater");
        }

        @Override
        public void run()
        {
            while (true)
            {
                try { Thread.sleep(1000); }
                catch (InterruptedException e)
                {
                    dialog.dispose();
                    break;
                }

                dialog.cycle();
            }
        }

        public void updateDialog(String newTask)
        {
            dialog.updateTask(newTask);
        }
    }
}


<|start_filename|>src/io/github/keheck/tree/NavTreeModelListener.java<|end_filename|>
package io.github.keheck.tree;

import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;

public class NavTreeModelListener implements TreeModelListener
{
    @Override
    public void treeNodesChanged(TreeModelEvent e)
    {

    }

    @Override
    public void treeNodesInserted(TreeModelEvent e)
    {
        AbstractNavTreeNode node = (AbstractNavTreeNode)e.getChildren()[e.getChildren().length-1];
        node.setParent((AbstractNavTreeNode)e.getPath()[e.getPath().length-1]);
    }

    @Override
    public void treeNodesRemoved(TreeModelEvent e)
    {
        AbstractNavTreeNode node = (AbstractNavTreeNode)e.getChildren()[e.getChildren().length-1];
        node.setParent(null);
    }

    @Override
    public void treeStructureChanged(TreeModelEvent e)
    {

    }
}


<|start_filename|>src/io/github/keheck/util/OSUtils.java<|end_filename|>
package io.github.keheck.util;

import java.io.File;

public class OSUtils
{
    private static OS os;
    static File dataDir;
    static File mcDir;

    public static void init()
    {
        os = initOs();
        initDataDirs();
    }

    private static void initDataDirs()
    {
        String userHome = System.getProperty("user.home", ".");

        switch(os)
        {
            case MAC:
                dataDir = new File(userHome, "Library/Application Support/" + Constants.DIRECTORY);
                mcDir = new File(userHome, "Library/Application Support/minecraft");
                break;
            case WINDOWS:
                String appData = System.getenv("APPDATA");
                if(appData != null)
                {
                    dataDir = new File(appData, "." + Constants.DIRECTORY);
                    mcDir = new File(appData, ".minecraft");
                }
                else
                {
                    dataDir = new File(userHome, "." + Constants.DIRECTORY);
                    mcDir = new File(userHome, ".minecraft");
                }
                break;
            case LINUX:
            case SOLARIS:
                dataDir = new File(userHome, "." + Constants.DIRECTORY);
                mcDir = new File(userHome, ".minecraft");
                break;
            case UNKNOWN:
            default:
                dataDir = new File(userHome, "." + Constants.DIRECTORY);
                mcDir = new File(userHome, ".minecraft");
                break;
        }

        if(!dataDir.exists())
            dataDir.mkdirs();
    }

    private static OS initOs()
    {
        String os = System.getProperty("os.name").toLowerCase();

        if(os.contains("win"))
            return OS.WINDOWS;
        if(os.contains("mac"))
            return OS.MAC;
        if(os.contains("sunos") || os.contains("solaris"))
            return OS.SOLARIS;
        if(os.contains("linux") || os.contains("unix"))
            return OS.LINUX;

        return OS.UNKNOWN;
    }

    private enum OS
    {
        LINUX, WINDOWS, SOLARIS, MAC, UNKNOWN;

        public boolean isLinux() { return this == LINUX || this == SOLARIS; }

        public boolean isWin() { return this == WINDOWS; }

        public boolean isMac() { return this == MAC; }
    }
}


<|start_filename|>src/io/github/keheck/window/dialogs/DialogCompilationFail.java<|end_filename|>
package io.github.keheck.window.dialogs;

import io.github.keheck.Main;
import io.github.keheck.util.Directories;
import io.github.keheck.util.Log;

import javax.swing.*;
import java.awt.*;

public class DialogCompilationFail extends JDialog
{
    private JButton ok;

    public DialogCompilationFail()
    {
        super(Main.frame, "Compilation finisehd!");
        Log.i("Opening dialog DialogCompilationFail");
        JLabel label = new JLabel("<html><body>Compilation has failed! View the error log at " + "<br>" + Directories.rootDir.getAbsolutePath() + "/error.txt <br> for more information</body></html>");
        ok = new JButton("Ok");
        JPanel host = new JPanel();
        this.add(host);

        GroupLayout layout = new GroupLayout(host);
        host.setLayout(layout);

        layout.setVerticalGroup(layout.createSequentialGroup()
                .addComponent(label)
                .addComponent(ok));

        layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                .addComponent(label)
                .addComponent(ok));

        setLocationRelativeTo(null);
        setResizable(false);
        setSize(new Dimension(400, 200));
        setupActions();
        setVisible(true);
    }

    private void setupActions() { ok.addActionListener(e -> dispose()); }
}


<|start_filename|>src/io/github/keheck/project/exceptions/ExpectedTokenException.java<|end_filename|>
package io.github.keheck.project.exceptions;

public class ExpectedTokenException extends SyntaxException
{
    public ExpectedTokenException(String syntaxError, int lineNum, String function)
    {
        super("Expected token: " + syntaxError, lineNum, function);
    }
}


<|start_filename|>src/io/github/keheck/window/dialogs/DialogErrorNamespaceReserved.java<|end_filename|>
package io.github.keheck.window.dialogs;

import io.github.keheck.Main;
import io.github.keheck.util.Log;

import javax.swing.*;
import java.awt.*;

public class DialogErrorNamespaceReserved extends JDialog
{
    private JButton ok;

    public DialogErrorNamespaceReserved(String name, JDialog dialog)
    {
        super(dialog, "Namaspace Reserved!");

        JPanel panel = new JPanel();
        this.add(panel);

        Log.i("Opening dialog DialogErrorNamespaceReserved");
        ok = new JButton("Ok");
        JLabel label = new JLabel("This name is alreade reserved for compilation namespaces:");
        JPanel textWrapper = new JPanel();
        JLabel area = new JLabel(name);
        textWrapper.setBackground(new Color(255, 255, 255));
        area.setForeground(new Color(0xA40000));
        textWrapper.add(area);

        GroupLayout layout = new GroupLayout(panel);
        panel.setLayout(layout);

        layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                .addComponent(label)
                .addComponent(textWrapper, GroupLayout.Alignment.CENTER)
                .addComponent(ok)
        );

        layout.setVerticalGroup(layout.createSequentialGroup()
                .addComponent(label)
                .addGap(10)
                .addComponent(textWrapper, 25, 25, 25)
                .addComponent(ok)
        );

        setMinimumSize(new Dimension(350, 125));
        setResizable(false);
        setLocationRelativeTo(null);
        setupActions();
        setVisible(true);
    }

    private void setupActions() { ok.addActionListener(e -> setVisible(false)); }
}


<|start_filename|>src/io/github/keheck/project/exceptions/WrappedCompilationException.java<|end_filename|>
package io.github.keheck.project.exceptions;

public class WrappedCompilationException extends CompilationException
{
    public WrappedCompilationException(int lineNum, Throwable reason, String function)
    {
        super(lineNum, reason.getClass().getName() + " " + reason.getMessage(), function, true);
        this.initCause(reason);
    }
}


<|start_filename|>src/io/github/keheck/tree/NavTreeRenderer.java<|end_filename|>
package io.github.keheck.tree;

import io.github.keheck.window.Icons;

import javax.swing.*;
import javax.swing.tree.DefaultTreeCellRenderer;
import java.awt.*;

public class NavTreeRenderer extends DefaultTreeCellRenderer
{
    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus)
    {
        super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
        setIcon(getIconFor(value));
        return this;
    }

    private static ImageIcon getIconFor(Object o)
    {
        if(o instanceof AbstractNavTreeNode)
        {
            return ((AbstractNavTreeNode) o).getIcon();
        }

        return Icons.FILE_UNKNOWN;
    }

    @Override
    public Icon getOpenIcon() { return Icons.TREE_COLLAPSE; }

    @Override
    public Icon getClosedIcon() { return Icons.TREE_EXPAND; }
}


<|start_filename|>src/io/github/keheck/project/exceptions/UnexpectedTokenException.java<|end_filename|>
package io.github.keheck.project.exceptions;

public class UnexpectedTokenException extends SyntaxException
{
    public UnexpectedTokenException(String syntaxError, int lineNum, String function)
    {
        super("Unexpected token: " + syntaxError, lineNum, function);
    }
}


<|start_filename|>src/io/github/keheck/window/dialogs/DialogNewFile.java<|end_filename|>
package io.github.keheck.window.dialogs;

import io.github.keheck.Main;
import io.github.keheck.Tasks;
import io.github.keheck.tree.AbstractNavTreeNode;
import io.github.keheck.util.Log;

import javax.swing.*;
import java.awt.*;

public class DialogNewFile extends JDialog
{
    private AbstractNavTreeNode.NodeType type;
    private JTextField field;
    private JButton approve;
    private JButton cancel;
    private AbstractNavTreeNode node;

    public DialogNewFile(AbstractNavTreeNode.NodeType type, AbstractNavTreeNode parent)
    {
        super(Main.frame, "New File");
        JPanel panel = new JPanel();
        this.add(panel);
        this.type = type;
        node = parent;

        Log.i("Opening dialog DialogNewFile");
        JLabel label = new JLabel("File name");
        field = new JTextField();

        approve = new JButton("Create");
        cancel = new JButton("Cancel");

        GroupLayout layout = new GroupLayout(panel);
        panel.setLayout(layout);

        layout.setAutoCreateGaps(true);
        layout.setAutoCreateContainerGaps(true);

        layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                .addGroup(layout.createSequentialGroup()
                        .addComponent(label)
                        .addComponent(field))
                .addGroup(layout.createSequentialGroup()
                        .addComponent(approve)
                        .addComponent(cancel))
        );

        layout.setVerticalGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
                        .addComponent(label)
                        .addComponent(field, 30, 30, 30))
                .addGap(70)
                .addGroup(layout.createParallelGroup()
                        .addComponent(approve)
                        .addComponent(cancel))
        );

        setMinimumSize(new Dimension(400, 200));
        setResizable(false);
        setLocationRelativeTo(null);
        setupActions();
        setVisible(true);
    }

    private void setupActions()
    {
        approve.addActionListener(e -> Tasks.createNewFile(field.getText(), type, node, this));
        cancel.addActionListener(e -> dispose());
    }
}


<|start_filename|>src/io/github/keheck/window/dialogs/DialogLoadProjFilter.java<|end_filename|>
package io.github.keheck.window.dialogs;

import javax.swing.filechooser.FileFilter;
import java.io.File;

public class DialogLoadProjFilter extends FileFilter
{
    @Override
    public boolean accept(File f) { return f.isDirectory(); }

    @Override
    public String getDescription() { return "Root folder of the project"; }
}
