/*
* 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.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.minecraftforge.gradle.common.Constants;
import net.minecraftforge.gradle.util.delayed.DelayedFile;
import net.minecraftforge.gradle.util.mcp.JavadocAdder;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.ParallelizableTask;
import au.com.bytecode.opencsv.CSVReader;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
@ParallelizableTask
public class RemapSources extends AbstractEditJarTask
{
@InputFile
private DelayedFile methodsCsv;
@InputFile
private DelayedFile fieldsCsv;
@InputFile
private DelayedFile paramsCsv;
private boolean addsJavadocs = true;
private final Map<String, String> methods = Maps.newHashMap();
private final Map<String, String> methodDocs = Maps.newHashMap();
private final Map<String, String> fields = Maps.newHashMap();
private final Map<String, String> fieldDocs = Maps.newHashMap();
private final Map<String, String> params = Maps.newHashMap();
private static final Pattern SRG_FINDER = Pattern.compile("(func_[0-9]+_[a-zA-Z_]+|field_[0-9]+_[a-zA-Z_]+|p_[\\w]+_\\d+_)([^\\w\\$])");
private static final Pattern METHOD = Pattern.compile("^((?: {4})+|\\t+)(?!return)(?:[\\w$.\\[\\]]+(?:<.+>)? )+(func_[0-9]+_[a-zA-Z_]+)\\(");
private static final Pattern FIELD = Pattern.compile("^((?: {4})+|\\t+)(?:[\\w$.\\[\\]]+(?:<.+>)? )+(field_[0-9]+_[a-zA-Z_]+) *(?:=|;)");
@Override
public void doStuffBefore() throws Exception
{
// read CSV files
CSVReader reader = Constants.getReader(getMethodsCsv());
for (String[] s : reader.readAll())
{
methods.put(s[0], s[1]);
if (!s[3].isEmpty() && addsJavadocs)
methodDocs.put(s[0], s[3]);
}
reader = Constants.getReader(getFieldsCsv());
for (String[] s : reader.readAll())
{
fields.put(s[0], s[1]);
if (!s[3].isEmpty() && addsJavadocs)
fieldDocs.put(s[0], s[3]);
}
reader = Constants.getReader(getParamsCsv());
for (String[] s : reader.readAll())
{
params.put(s[0], s[1]);
}
}
@Override
protected boolean storeJarInRam()
{
return false;
}
@Override
public String asRead(String name, String text)
{
ArrayList<String> newLines = new ArrayList<String>();
for (String line : Constants.lines(text))
{
// basically all this code is to find the javadocs for a field ebfore replacing it.
// if we arnt doing javadocs.. screw dat.
if (addsJavadocs)
{
injectJavadoc(newLines, line);
}
newLines.add(replaceInLine(line));
}
return Joiner.on(Constants.NEWLINE).join(newLines);
}
private void injectJavadoc(List<String> newLines, String line)
{
// methods
Matcher matcher = METHOD.matcher(line);
if (matcher.find())
{
String javadoc = methodDocs.get(matcher.group(2));
if (!Strings.isNullOrEmpty(javadoc))
{
insetAboveAnnotations(newLines, JavadocAdder.buildJavadoc(matcher.group(1), javadoc, true));
}
// worked, so return and dont try the fields.
return;
}
// fields
matcher = FIELD.matcher(line);
if (matcher.find())
{
String javadoc = fieldDocs.get(matcher.group(2));
if (!Strings.isNullOrEmpty(javadoc))
{
insetAboveAnnotations(newLines, JavadocAdder.buildJavadoc(matcher.group(1), javadoc, false));
}
}
}
private static void insetAboveAnnotations(List<String> list, String line)
{
int back = 0;
while (list.get(list.size() - 1 - back).trim().startsWith("@"))
{
back++;
}
list.add(list.size() - back, line);
}
private String replaceInLine(String line)
{
// FAR all methods
StringBuffer buf = new StringBuffer();
Matcher matcher = SRG_FINDER.matcher(line);
while (matcher.find())
{
String find = matcher.group(1);
if (find.startsWith("p_"))
find = params.get(find);
else if (find.startsWith("func_"))
find = methods.get(find);
else if (find.startsWith("field_"))
find = fields.get(find);
if (find == null)
find = matcher.group(1);
matcher.appendReplacement(buf, find);
buf.append(matcher.group(2));
}
matcher.appendTail(buf);
return buf.toString();
}
public File getMethodsCsv()
{
return methodsCsv.call();
}
public void setMethodsCsv(DelayedFile methodsCsv)
{
this.methodsCsv = methodsCsv;
}
public File getFieldsCsv()
{
return fieldsCsv.call();
}
public void setFieldsCsv(DelayedFile fieldsCsv)
{
this.fieldsCsv = fieldsCsv;
}
public File getParamsCsv()
{
return paramsCsv.call();
}
public void setParamsCsv(DelayedFile paramsCsv)
{
this.paramsCsv = paramsCsv;
}
public boolean addsJavadocs()
{
return addsJavadocs;
}
public void setAddsJavadocs(boolean javadoc)
{
this.addsJavadocs = javadoc;
}
@Override
public void doStuffMiddle(Map<String, String> sourceMap, Map<String, byte[]> resourceMap) throws Exception { }
@Override
public void doStuffAfter() throws Exception { }
}