net.minecraftforge.gradle.tasks.PostDecompileTask.java Source code

Java tutorial

Introduction

Here is the source code for net.minecraftforge.gradle.tasks.PostDecompileTask.java

Source

/*
 * A Gradle plugin for the creation of Minecraft mods and MinecraftForge plugins.
 * Copyright (C) 2013 Minecraft Forge
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
 * USA
 */
package net.minecraftforge.gradle.tasks;

import java.io.File;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import net.minecraftforge.gradle.common.Constants;
import net.minecraftforge.gradle.util.caching.Cached;
import net.minecraftforge.gradle.util.mcp.FFPatcher;
import net.minecraftforge.gradle.util.mcp.FmlCleanup;
import net.minecraftforge.gradle.util.mcp.GLConstantFixer;
import net.minecraftforge.gradle.util.mcp.McpCleanup;
import net.minecraftforge.gradle.util.patching.ContextualPatch;
import net.minecraftforge.gradle.util.patching.ContextualPatch.HunkReport;
import net.minecraftforge.gradle.util.patching.ContextualPatch.PatchReport;
import net.minecraftforge.gradle.util.patching.ContextualPatch.PatchStatus;

import org.gradle.api.file.FileCollection;
import org.gradle.api.logging.LogLevel;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.OutputFile;

import com.github.abrarsyed.jastyle.ASFormatter;
import com.github.abrarsyed.jastyle.OptParser;
import com.google.common.base.Joiner;
import com.google.common.base.Throwables;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.io.Files;

public class PostDecompileTask extends AbstractEditJarTask {
    @InputFile
    private Object inJar;

    private Object patchDir;

    @InputFile
    private Object astyleConfig;

    @OutputFile
    @Cached
    private Object outJar;

    private static final Pattern BEFORE = Pattern
            .compile("(?m)((case|default).+(?:\\r\\n|\\r|\\n))(?:\\r\\n|\\r|\\n)");
    private static final Pattern AFTER = Pattern
            .compile("(?m)(?:\\r\\n|\\r|\\n)((?:\\r\\n|\\r|\\n)[ \\t]+(case|default))");

    private final Multimap<String, File> patchesMap = ArrayListMultimap.create();
    private final List<PatchReport> patchErrors = Lists.newArrayList();
    private final ASFormatter formatter = new ASFormatter();
    private GLConstantFixer oglFixer;

    @Override
    public void doStuffBefore() throws Exception {
        for (File f : getPatches()) {
            String name = f.getName();

            if (name.contains("Enum")) // because version of FF is awesome and dont need dat
                continue;

            int patchIndex = name.indexOf(".patch");

            // 6 is the length of ".patch" + 3 to account for .## at the end of the file.
            if (patchIndex < 0 || patchIndex < name.length() - 9)
                continue;

            patchesMap.put(name.substring(0, patchIndex), f);
        }

        OptParser parser = new OptParser(formatter);
        parser.parseOptionFile(getAstyleConfig());

        oglFixer = new GLConstantFixer();
    }

    @Override
    public String asRead(String name, String file) throws Exception {
        getLogger().debug("Processing file: " + file);

        file = FFPatcher.processFile(file);

        // patch the file
        Collection<File> patchFiles = patchesMap.get(name.replace('/', '.'));
        if (!patchFiles.isEmpty()) {
            getLogger().debug("applying MCP patches");
            ContextProvider provider = new ContextProvider(file);
            ContextualPatch patch = findPatch(patchFiles, provider);
            patchErrors.addAll(patch.patch(false));
            file = provider.getAsString();
        }

        getLogger().debug("processing comments");
        file = McpCleanup.stripComments(file);

        getLogger().debug("fixing imports comments");
        file = McpCleanup.fixImports(file);

        getLogger().debug("various other cleanup");
        file = McpCleanup.cleanup(file);

        getLogger().debug("fixing OGL constants");
        file = oglFixer.fixOGL(file);

        getLogger().debug("formatting source");
        Reader reader = new StringReader(file);
        Writer writer = new StringWriter();
        formatter.format(reader, writer);
        reader.close();
        writer.flush();
        writer.close();
        file = writer.toString();

        getLogger().debug("applying FML transformations");
        file = BEFORE.matcher(file).replaceAll("$1");
        file = AFTER.matcher(file).replaceAll("$1");
        file = FmlCleanup.renameClass(file);

        return file;
    }

    @Override
    public void doStuffAfter() throws Exception {
        boolean fuzzed = false;
        for (PatchReport report : patchErrors) {
            if (!report.getStatus().isSuccess()) {
                //getLogger().log(LogLevel.ERROR, "Patching failed: " + report.getTarget(), report.getFailure());
                getLogger().error("Patching failed: " + report.getTarget());

                for (HunkReport hunk : report.getHunks()) {
                    if (!hunk.getStatus().isSuccess()) {
                        getLogger().error("Hunk " + hunk.getHunkID() + " failed!");
                    }
                }

                Throwables.propagate(report.getFailure());
            } else if (report.getStatus() == PatchStatus.Fuzzed) // catch fuzzed patches
            {
                getLogger().log(LogLevel.INFO, "Patching fuzzed: " + report.getTarget(), report.getFailure());
                fuzzed = true;

                for (HunkReport hunk : report.getHunks()) {
                    if (!hunk.getStatus().isSuccess()) {
                        getLogger().info("Hunk " + hunk.getHunkID() + " fuzzed " + hunk.getFuzz() + "!");
                    }
                }
            } else {
                getLogger().debug("Patch succeeded: " + report.getTarget());
            }
        }
        if (fuzzed)
            getLogger().lifecycle("Patches Fuzzed!");
    }

    private static ContextualPatch findPatch(Collection<File> files, ContextProvider provider) throws Exception {
        ContextualPatch patch = null;
        for (File f : files) {
            patch = ContextualPatch.create(Files.toString(f, Constants.CHARSET), provider);
            List<PatchReport> errors = patch.patch(true);

            boolean success = true;
            for (PatchReport rep : errors) {
                if (!rep.getStatus().isSuccess())
                    success = false;
            }
            if (success)
                break;
        }
        return patch;
    }

    public File getAstyleConfig() {
        return getProject().file(astyleConfig);
    }

    public void setAstyleConfig(Object astyleConfig) {
        this.astyleConfig = astyleConfig;
    }

    @InputFiles
    public FileCollection getPatches() {
        return getProject().fileTree(patchDir);
    }

    public void setPatches(Object patchesDir) {
        this.patchDir = patchesDir;
    }

    /**
     * A private inner class to be used with the MCPPatches only.
     */
    private static class ContextProvider implements ContextualPatch.IContextProvider {
        private List<String> data;

        public ContextProvider(String file) {
            data = Constants.lines(file);
        }

        @Override
        public List<String> getData(String target) {
            List<String> out = new ArrayList<String>(data.size() + 5);
            out.addAll(data);
            return out;
        }

        @Override
        public void setData(String target, List<String> data) {
            this.data = data;
        }

        public String getAsString() {
            return Joiner.on(Constants.NEWLINE).join(data);
        }
    }

    //@formatter:off
    @Override
    public void doStuffMiddle(Map<String, String> sourceMap, Map<String, byte[]> resourceMap) throws Exception {
    }

    @Override
    protected boolean storeJarInRam() {
        return false;
    }
}