meff.Function.java Source code

Java tutorial

Introduction

Here is the source code for meff.Function.java

Source

/*
 * This file is part of Minecraft Editor For Functions.
 * Copyright (c) 2017 suncrasher <suncrasher@outlook.com>
 *
 * Minecraft Editor For Functions is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Minecraft Editor For Functions is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Minecraft Editor For Functions.  If not, see <http://www.gnu.org/licenses/>.
 */

package meff;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;

import com.google.gson.Gson;

public class Function {
    private final String id; // Format: world;namespace:path/to/func

    private FunctionData data;

    private final Settings s;

    public Function(String id, Settings settings) {
        this.id = id;
        s = settings;

        data = new FunctionData();
    }

    public boolean load() {
        File file = getFile(true);
        if (file.exists()) {
            data = getGson().fromJson(U.readFileToOneLine(file.getAbsolutePath()), FunctionData.class);
        } else {
            file = getFile(false);
            if (file.exists()) {
                data = new FunctionData();
                data.lines = U.readFile(file.getAbsolutePath());
            } else {
                data = new FunctionData();
                save();
            }
        }
        return true;
    }

    public boolean save() {
        boolean success = true;

        Map<String, String> bin = compile();

        if (!getFile(true).getParentFile().exists())
            getFile(true).getParentFile().mkdirs();
        success &= U.saveFile(getFile(true).getAbsolutePath(), getGson().toJson(data));

        File dir = getFile().getParentFile();
        for (Map.Entry<String, String> entry : bin.entrySet()) {
            success &= U.saveFile(new File(dir, entry.getKey()).getAbsolutePath(), entry.getValue());
        }

        File advFile = getAdvFile();
        if (data.adv == Advancement.NONE) {
            if (advFile.exists())
                advFile.delete();
            return success;
        }

        String trigger;
        switch (data.adv) {
        case TICK:
            trigger = "minecraft:tick";
            break;
        case SECOND:
            trigger = "minecraft:location";
            break;
        default:
            trigger = "minecraft:impossible";
        }
        String advBody = "{\"criteria\":{\"" + getHalfId() + "\":{\"trigger\":\"" + trigger
                + "\"}}, \"rewards\":{\"function\":\"" + getHalfId() + "\"}}";
        if (!advFile.getParentFile().exists())
            advFile.getParentFile().mkdirs();
        success &= U.saveFile(advFile.getAbsolutePath(), advBody);
        return success;
    }

    private Map<String, String> compile() {
        class Compiler {
            private Map<String, StringBuilder> functions;
            private String currentFunctionName;
            private StringBuilder currentFunction;

            Compiler(String functionName) {
                functions = new HashMap<>();
                currentFunctionName = functionName;
                currentFunction = new StringBuilder();
                functions.put(currentFunctionName, currentFunction);
            }

            void compileLine(StringBuilder line) {
                // Manage score value features
                {
                    Matcher m = Pattern.compile("(score_\\w+)(>=|>|<=|<|==)(-?\\d+)").matcher(line);
                    while (m.find()) {
                        int offset = m.start();

                        String beginning = m.group(1);
                        String operator = m.group(2);
                        String end = m.group(3);

                        StringBuilder newScore = new StringBuilder();
                        switch (operator) {
                        case ">=":
                            newScore.append(beginning).append("_min=").append(end);
                            break;
                        case ">":
                            newScore.append(beginning).append("_min=").append(Integer.parseInt(end) + 1);
                            break;
                        case "<=":
                            newScore.append(beginning).append("=").append(end);
                            break;
                        case "<":
                            newScore.append(beginning).append("=").append(Integer.parseInt(end) - 1);
                            break;
                        case "==":
                            newScore.append(beginning).append("_min=").append(end).append(",").append(beginning)
                                    .append("=").append(end);
                            break;
                        }

                        line.replace(offset, offset + m.group().length(), newScore.toString());

                        m.reset(); // Need to reset the matcher, so it updates the length of the text
                    }
                }

                currentFunction.append(line).append("\n");
            }

            Map<String, String> getOutput() {
                Map<String, String> outputMap = new HashMap<>(functions.size());

                for (Map.Entry<String, StringBuilder> entry : functions.entrySet()) {
                    outputMap.put(entry.getKey() + ".mcfunction", entry.getValue().toString());
                }

                return outputMap;
            }
        }

        Compiler compiler = new Compiler(getName());

        boolean comment = false;

        StringBuilder sb = new StringBuilder();

        for (String line : data.lines) {
            line = line.trim();
            if (line.startsWith("#") || line.startsWith("//")) {
                continue;
            }
            if (line.isEmpty()) {
                continue;
            }

            int si = 0; // start index
            int bci = -1; // block comment index
            int bcei = -1; // block comment end index
            int prevBci = -1;
            int prevBcei = -1;
            while ((!comment && (bci = line.indexOf("/*", si)) > -1)
                    || (comment && (bcei = line.indexOf("*/", si)) > -1)) {
                if (comment) {
                    if (line.charAt(bcei - 1) == '\\') {
                        si = bcei + 2;
                        bcei = prevBcei;
                        continue;
                    }
                    comment = false;
                    si = bcei + 2;
                } else {
                    if (line.charAt(bci - 1) == '\\') {
                        sb.append(line.substring(si, bci - 1)).append("/*");
                        si = bci + 2;
                        bci = prevBci;
                        continue;
                    }
                    sb.append(line.substring(si, bci));
                    comment = true;
                    si = bci + 2;
                }
                prevBci = bci;
                prevBcei = bcei;
            }

            if (comment) {
                continue;
            }

            if (line.endsWith("\\")) {
                line = line.substring(si, line.length() - 1);
                sb.append(line);
            } else {
                sb.append(line.substring(si));
                compiler.compileLine(sb);
                sb.delete(0, sb.length());
            }
        }

        return compiler.getOutput();
    }

    public void delete() {
        try {
            File f = getFile(false);
            if (f.isDirectory()) {
                FileUtils.deleteDirectory(f);
                FileUtils.deleteDirectory(getFile(true));
                FileUtils.deleteDirectory(getAdvFile().getParentFile());
            } else {
                String prefix = getName() + "$";
                f.delete();
                f = f.getParentFile();
                for (File ff : f.listFiles())
                    if (ff.getName().startsWith(prefix))
                        ff.delete();
                f = getFile(true);
                if (f.exists())
                    f.delete();
                f = getAdvFile();
                if (f.exists())
                    f.delete();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public boolean isFunction() {
        return id.contains(";");
    }

    public void setLines(List<String> lines) {
        data.lines = lines;
    }

    public List<String> getLines() {
        return data.lines;
    }

    public void setAdv(Advancement adv) {
        data.adv = adv;
    }

    public Advancement getAdv() {
        return data.adv;
    }

    public String getId() {
        return id;
    }

    public String getHalfId() {
        String[] split = id.split(";");
        if (split.length == 1)
            return "";
        String half = split[1];
        if (half.endsWith(":"))
            half = half.substring(0, half.length() - 1);
        return half;
    }

    public File getFile() {
        return getFile(false);
    }

    public File getAdvFile() {
        String[] split = id.split(";");
        File f = new File(s.minecraftSavesLocation.getAbsolutePath() + "/" + split[0] + "/data/advancements/"
                + split[1].replace(":", "/"));
        if (f.isDirectory())
            return f;
        return new File(f.getAbsolutePath() + ".json");
    }

    public File getFile(boolean source) {
        if (source) {
            String[] split = id.split(";");
            File f = new File(s.minecraftSavesLocation.getAbsolutePath() + "/" + split[0] + "/data/functions/.meff/"
                    + split[1].replace(":", "/"));
            if (f.isDirectory())
                return f;
            return new File(f.getAbsolutePath() + ".json");
        }
        String[] split = id.split(";");
        if (split.length > 1) {
            String world = split[0];

            String funcPath = split[1].replace(':', '/');

            File file = new File(
                    s.minecraftSavesLocation.getAbsolutePath() + "/" + world + "/data/functions/" + funcPath);

            if (file.isDirectory())
                return file;
            else
                return new File(file.getAbsolutePath() + ".mcfunction");
        }
        return new File(s.minecraftSavesLocation.getAbsolutePath() + "/" + split[0] + "/data/functions");
    }

    public String getName() {
        if (id.contains("/")) {
            return id.substring(id.lastIndexOf('/') + 1);
        }
        if (!id.contains(":")) {
            return id;
        }
        if (id.length() > id.indexOf(":") + 1) {
            return id.split(":")[1];
        }
        return id.split(";")[1].split(":")[0];
    }

    @Override
    public String toString() {
        return getName();
    }

    private Gson getGson() {
        return new Gson();
    }

    public enum Advancement {
        NONE, TICK, SECOND
    }

    private static class FunctionData {
        List<String> lines;
        Advancement adv;

        FunctionData() {
            lines = new ArrayList<>();
            lines.add("# Your commands here");
            adv = Advancement.NONE;
        }
    }
}