Tabulate crash report mod list and add signature information (#4251)

This commit is contained in:
Marvin Rösch 2017-08-19 00:28:58 +02:00 committed by LexManos
parent a40df67004
commit 43e24c3eb4
4 changed files with 364 additions and 5 deletions

View file

@ -414,6 +414,11 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
all.add(asm.getClassName());
for (ASMData asm : evt.getASMHarvestedData().getAll(ICrashCallable.class.getName().replace('.', '/')))
all.add(asm.getClassName());
// Add table classes for mod list tabulation
all.add("net/minecraftforge/common/util/TextTable");
all.add("net/minecraftforge/common/util/TextTable$Column");
all.add("net/minecraftforge/common/util/TextTable$Row");
all.add("net/minecraftforge/common/util/TextTable$Alignment");
all.removeIf(cls -> !cls.startsWith("net/minecraft/") && !cls.startsWith("net/minecraftforge/"));

View file

@ -0,0 +1,220 @@
/*
* Minecraft Forge
* Copyright (c) 2016.
*
* 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 version 2.1
* of the License.
*
* 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.common.util;
import com.google.common.collect.Streams;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* Utility to format data into a textual (markdown-compliant) table.
*/
public class TextTable
{
public static Column column(String header)
{
return new Column(header);
}
public static Column column(String header, Alignment alignment)
{
return new Column(header, alignment);
}
private final List<Column> columns;
private final List<Row> rows = new ArrayList<>();
public TextTable(List<Column> columns)
{
this.columns = columns;
}
public String build(String lineEnding)
{
StringBuilder destination = new StringBuilder();
append(destination, lineEnding);
return destination.toString();
}
/**
* Appends the data formatted as a table to the given string builder.
* The padding character used for the column alignments is a single space (' '),
* the separate between column headers and values is a dash ('-').
* Note that you *have* to specify a line ending, '\n' isn't used by default.
* <p>
* The generated table is compliant with the markdown file format.
*
* @param destination a string builder to append the table to
* @param lineEnding the line ending to use for each row of the table
*/
public void append(StringBuilder destination, String lineEnding)
{
List<String> headers = columns.stream().map(c -> c.formatHeader(" ")).collect(Collectors.toList());
printRow(destination, headers);
destination.append(lineEnding);
printSeparators(destination);
for (Row row : rows)
{
destination.append(lineEnding);
printRow(destination, row.format(columns, " "));
}
}
private void printSeparators(StringBuilder destination)
{
destination.append('|');
for (Column column : columns)
{
destination.append(column.alignment != Alignment.RIGHT ? ':' : ' ');
destination.append(column.getSeparator('-'));
destination.append(column.alignment != Alignment.LEFT ? ':' : ' ');
destination.append('|');
}
}
private void printRow(StringBuilder destination, List<String> values)
{
destination.append('|');
for (String value : values)
{
destination.append(' ');
destination.append(value);
destination.append(' ');
destination.append('|');
}
}
public void add(@Nonnull Object... values)
{
if (values.length != columns.size())
{
throw new IllegalArgumentException("Received wrong amount of values for table row, expected " + columns.size() + ", received " + columns.size() + ".");
}
Row row = new Row();
for (int i = 0; i < values.length; i++)
{
String value = Objects.toString(values[i]);
row.values.add(value);
columns.get(i).fit(value);
}
rows.add(row);
}
public void clear()
{
for (Column column : columns)
{
column.resetWidth();
}
rows.clear();
}
public List<Column> getColumns()
{
return Collections.unmodifiableList(columns);
}
public static class Column
{
private String header;
private int width;
private Alignment alignment;
public Column(String header)
{
this(header, Alignment.LEFT);
}
public Column(String header, Alignment alignment)
{
this.header = header;
this.width = header.length();
this.alignment = alignment;
}
public String formatHeader(String padding)
{
return format(header, padding);
}
public String format(String value, String padding)
{
switch (alignment)
{
case LEFT:
return StringUtils.rightPad(value, width, padding);
case RIGHT:
return StringUtils.leftPad(value, width, padding);
default:
int length = value.length();
int left = (width - length) / 2;
int leftWidth = left + length;
return StringUtils.rightPad(StringUtils.leftPad(value, leftWidth, padding), width, padding);
}
}
public String getSeparator(char character)
{
return StringUtils.leftPad("", width, character);
}
public void fit(String value)
{
if (value.length() > width)
{
width = value.length();
}
}
public void resetWidth()
{
this.width = header.length();
}
public int getWidth()
{
return width;
}
}
public static class Row
{
private final ArrayList<String> values = new ArrayList<>();
public List<String> format(List<Column> columns, String padding)
{
if (columns.size() != values.size())
{
throw new IllegalArgumentException("Received wrong amount of columns for table row, expected " + columns.size() + ", received " + columns.size() + ".");
}
return Streams.zip(values.stream(), columns.stream(), (v, c) -> c.format(v, padding)).collect(Collectors.toList());
}
}
public enum Alignment
{
LEFT, CENTER, RIGHT
}
}

View file

@ -28,6 +28,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import net.minecraftforge.common.util.TextTable;
import net.minecraftforge.fml.common.LoaderState.ModState;
import net.minecraftforge.fml.common.ProgressManager.ProgressBar;
import net.minecraftforge.fml.common.event.FMLEvent;
@ -310,14 +311,28 @@ public class LoadController
for (ModState state : ModState.values())
ret.append(" '").append(state.getMarker()).append("' = ").append(state.toString());
TextTable table = new TextTable(Lists.newArrayList(
TextTable.column("State"),
TextTable.column("ID"),
TextTable.column("Version"),
TextTable.column("Source"),
TextTable.column("Signature"))
);
for (ModContainer mc : loader.getModList())
{
ret.append("\n\t");
for (ModState state : modStates.get(mc.getModId()))
ret.append(state.getMarker());
ret.append("\t").append(mc.getModId()).append("{").append(mc.getVersion()).append("} [").append(mc.getName()).append("] (").append(mc.getSource().getName()).append(") ");
table.add(
modStates.get(mc.getModId()).stream().map(ModState::getMarker).reduce("", (a, b) -> a + b),
mc.getModId(),
mc.getVersion(),
mc.getSource().getName(),
mc.getSigningCertificate() != null ? CertificateHelper.getFingerprint(mc.getSigningCertificate()) : "None"
);
}
ret.append("\n");
ret.append("\n\t");
table.append(ret, "\n\t");
ret.append("\n");
}
public List<ModContainer> getActiveModList()

View file

@ -0,0 +1,119 @@
/*
* Minecraft Forge
* Copyright (c) 2016.
*
* 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 version 2.1
* of the License.
*
* 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.test;
import com.google.common.collect.Lists;
import net.minecraftforge.common.util.TextTable;
import net.minecraftforge.common.util.TextTable.Column;
import org.junit.Assert;
import org.junit.Test;
import static net.minecraftforge.common.util.TextTable.column;
public class TextTableTest
{
private static final String WIDTH_REFERENCE = "StringOfWidth15";
private static final int WIDTH_REFERENCE_LENGTH = WIDTH_REFERENCE.length();
@Test
public void testColumnWidthAdjustment()
{
Column column = column("Column", TextTable.Alignment.LEFT);
column.fit(WIDTH_REFERENCE);
String paddedHeader = column.formatHeader("-");
Assert.assertEquals("Formatted column header didn't have correct length", WIDTH_REFERENCE_LENGTH, paddedHeader.length());
Assert.assertEquals("Formatted column header wasn't padded properly", "Column---------", paddedHeader);
String paddedReference = column.format(WIDTH_REFERENCE, "-");
Assert.assertEquals("Formatted width reference didn't have correct length", WIDTH_REFERENCE_LENGTH, paddedReference.length());
Assert.assertEquals("Formatted width reference was changed despite defining width", WIDTH_REFERENCE, paddedReference);
}
@Test
public void testLeftAlignment()
{
Column column = column("Left", TextTable.Alignment.LEFT);
column.fit(WIDTH_REFERENCE);
String paddedHeader = column.formatHeader("-");
Assert.assertEquals("Left-aligned header should be padded on the right", "Left-----------", paddedHeader);
String paddedReference = column.format(WIDTH_REFERENCE, "-");
Assert.assertEquals("Left-aligned reference should'nt be padded", WIDTH_REFERENCE, paddedReference);
String paddedValue = column.format("Value", "-");
Assert.assertEquals("Left-aligned value should be padded on the right", "Value----------", paddedValue);
}
@Test
public void testCenterAlignment()
{
Column column = column("Centered", TextTable.Alignment.CENTER);
column.fit(WIDTH_REFERENCE);
String paddedHeader = column.formatHeader("-");
Assert.assertEquals("Centered header should be padded equally on both sides", "---Centered----", paddedHeader);
String paddedReference = column.format(WIDTH_REFERENCE, "-");
Assert.assertEquals("Centered reference should'nt be padded", WIDTH_REFERENCE, paddedReference);
String paddedValue = column.format("Value", "-");
Assert.assertEquals("Centered value should be padded equally on both sides", "-----Value-----", paddedValue);
String paddedOffCenter = column.format("Value1", "-");
Assert.assertNotEquals("Center padding should be left-biased", "-----Value1----", paddedOffCenter);
}
@Test
public void testRightAlignment()
{
Column column = column("Right", TextTable.Alignment.RIGHT);
column.fit(WIDTH_REFERENCE);
String paddedHeader = column.formatHeader("-");
Assert.assertEquals("Right-aligned header should be padded on the left", "----------Right", paddedHeader);
String paddedReference = column.format(WIDTH_REFERENCE, "-");
Assert.assertEquals("Right-aligned reference should'nt be padded", WIDTH_REFERENCE, paddedReference);
String paddedValue = column.format("Value", "-");
Assert.assertEquals("Right-aligned value should be padded on the left", "----------Value", paddedValue);
}
@Test
public void testMarkdownCompliance()
{
TextTable table = new TextTable(Lists.newArrayList(
column("Left", TextTable.Alignment.LEFT),
column("Center", TextTable.Alignment.CENTER),
column("Right", TextTable.Alignment.RIGHT)
));
table.add("Long Value 1", "Value 2", "Value 3");
table.add("Value 1", "Long Value 2", "Value 3");
table.add("Value 1", "Value 2", "Long Value 3");
int[] columnWidths = table.getColumns().stream().mapToInt(Column::getWidth).toArray();
Assert.assertArrayEquals("Column widths should adjust for long values", new int[]{12, 12, 12}, columnWidths);
String[] result = table.build("\n").split("\n");
Assert.assertEquals("Header row + separator row + value rows should result in 5 lines", 5, result.length);
Assert.assertEquals(
"Column headers should be properly formatted",
"| Left | Center | Right |",
result[0]);
Assert.assertEquals(
"Header-body separators should contain markdown alignment information",
"|:------------ |:------------:| ------------:|",
result[1]
);
}
}