Java tutorial
/* * 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; } }